Trouble with IPFW or TCP?

Ian Smith smithi at nimnet.asn.au
Fri Apr 4 16:03:32 UTC 2008


On Thu, 3 Apr 2008, Julian Elischer wrote:
 > Ian Smith wrote:
 > > On Thu, 3 Apr 2008, Julian Elischer wrote:
 > >  > Ivan Voras wrote:
 > >  > > Erik Trulsson wrote:
 > >  > >> On Fri, Apr 04, 2008 at 01:34:07AM +0200, Ivan Voras wrote:
 > >  > >>> In which case would an ipfw ruleset like this:
 > >  > >>>
 > >  > >>> 00100 114872026  40487887607 allow ip from any to any via lo0
 > >  > >>> 00200         0            0 deny ip from any to 127.0.0.0/8
 > >  > >>> 00300         0            0 deny ip from 127.0.0.0/8 to any
 > >  > >>> 00600      1585       112576 deny ip from table(0) to me
 > >  > >>> 01000     90279      7325972 allow icmp from any to any
 > >  > >>> 05000 475961039 334422494257 allow tcp from me to any setup keep-state
 > >  > >>> 05100    634155     65779377 allow udp from me to any keep-state
 > >  > >>> 06022    409604     69177326 allow tcp from any to me dst-port 22 
 > >  > >>> setup keep-state
 > >  > >>> 06080  52159025  43182548092 allow tcp from any to me dst-port 80 
 > >  > >>> setup keep-state
 > >  > >>> 06443   6392366   2043532158 allow tcp from any to me dst-port 443 
 > >  > >>> setup keep-state
 > >  > >>> 07020    517065    292377553 allow tcp from any to me dst-port 8080 
 > >  > >>> setup keep-state
 > >  > >>> 65400  12273387    629703212 deny log ip from any to any
 > >  > >>> 65535         0            0 deny ip from any to any
 > >  > >>
 > >  > >> If you are using 'keep-state' should there not also be some rule 
 > >  > >> containing
 > >  > >> 'check-state' ?
 > >  > > 
 > >  > > Not according to the ipfw(8) manual:
 > >  > > 
 > >  > > """
 > >  > >      These dynamic rules, which have a limited lifetime, are checked at the
 > >  > >      first occurrence of a check-state, keep-state or limit rule, and 
 > >  > > are typ-
 > >  > >      ically used to open the firewall on-demand to legitimate traffic only.
 > >  > >      See the STATEFUL FIREWALL and EXAMPLES Sections below for more 
 > >  > > informa-
 > >  > >      tion on the stateful behaviour of ipfw.
 > >  > > """
 > >  > > 
 > >  > > I read this to mean the dynamic rules are checked at rule #5000 from the 
 > >  > > above list. Is there an advantage to having an explicit check-state rule 
 > >  > > in simple rulesets like this one?
 > >  > 
 > >  > the docs are wrong then I think.
 > > 
 > > If so, they've been wrong since 4.something .. certainly before 4.8. 
 > > It's hard to imagine nobody else has ever relied on that doc behaviour,
 > > so perhaps the docs, if wrong, have become so at some more recent time?
 > 
 > Not that I have known... keep-state does not (and never has) include
 > an implicit check-state.

Sorry (and surprised!) to have to differ, but you MADE me read the code!

Bearing in mind I'm reading 5.5 sources - stop me if it's changed - but
starting with /usr/sbin/ipfw/ipfw2.c we see that adding check-state just
generates an O_CHECK_STATE, while adding keep-state or limit rules first
generate an initial O_PROBE_STATE opcode (ignored when listing rules):

        /*
         * Now copy stuff into the rule.
         * If we have a keep-state option, the first instruction
         * must be a PROBE_STATE (which is generated here).
         * If we have a LOG option, it was stored as the first command,
         * and now must be moved to the top of the action part.
         */
        dst = (ipfw_insn *)rule->cmd;
[..]
        /*
         * generate O_PROBE_STATE if necessary
         */
        if (have_state && have_state->opcode != O_CHECK_STATE) {
                fill_cmd(dst, O_PROBE_STATE, 0, 0);
                dst = next_cmd(dst);

then go on to generate the O_KEEP_STATE or O_LIMIT rule as appropriate. 

Now in /sys/netinet/ip_fw2.c in ipfw_chk circa line 2400 (@5.5) we have: 

                         * O_LIMIT and O_KEEP_STATE: these opcodes are
                         *   not real 'actions', and are stored right
                         *   before the 'action' part of the rule.
                         *   These opcodes try to install an entry in the
                         *   state tables; if successful, we continue with
                         *   the next opcode (match=1; break;), otherwise
                         *   the packet *   must be dropped
                         *   ('goto done' after setting retval);
                         *
                         * 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
                         *   (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.
                         */
                        case O_LIMIT:
                        case O_KEEP_STATE:
                                if (install_state(f,
                                    (ipfw_insn_limit *)cmd, args)) {
                                        retval = IP_FW_PORT_DENY_FLAG;
                                        goto done; /* error/limit violation */
                                }
                                match = 1;
                                break;

                        case O_PROBE_STATE:
                        case O_CHECK_STATE:
                                /*
                                 * dynamic rules are checked at the first
                                 * keep-state or check-state occurrence,
                                 * with the result being stored in dyn_dir.
                                 * The compiler introduces a PROBE_STATE
                                 * instruction for us when we have a
                                 * KEEP_STATE (because PROBE_STATE needs
                                 * to be run first).
                                 */
                                if (dyn_dir == MATCH_UNKNOWN &&
                                    (q = lookup_dyn_rule(&args->f_id,
                                     &dyn_dir, proto == IPPROTO_TCP ?
                                        L3HDR(struct tcphdr, ip) : NULL))
                                        != NULL) {
                                        /*
                                         * Found dynamic entry, update stats
                                         * and jump to the 'action' part of
                                         * the parent rule.
                                         */
                                        q->pcnt++;
                                        q->bcnt += pktlen;
                                        f = q->rule;
                                        cmd = ACTION_PTR(f);
                                        l = f->cmd_len - f->act_ofs;
                                        IPFW_DYN_UNLOCK();
                                        goto check_body;
                                }
                                /*
                                 * Dynamic entry not found. If CHECK_STATE,
                                 * skip to next rule, if PROBE_STATE just
                                 * ignore and continue with next opcode.
                                 */
                                if (cmd->opcode == O_CHECK_STATE)
                                        goto next_rule;
                                match = 1;
                                break;

So indeed each rule with keep-state or limit options does the same probe
as a check-state in the first opcode, before then installing or checking
state in the subsequent opcode.  Or so it reads to an ancient neophyte ..

 > I think the document is talking about the  lifetime.
 > Each time a keep-state or check-state or limit is hit,
 > the TTL is kicked.

That's pretty well described under keep-state and elsewhere.  Good ol'
ipfw(8) has yet to let me down, and like Ivan I recall keep-state rules
(albeit only for UDP) without any check-state working just fine.

Not that any of that helps solve Ivan's problem ..

cheers, Ian



More information about the freebsd-net mailing list