Trouble with IPFW or TCP?

Ian Smith smithi at nimnet.asn.au
Sat Apr 5 09:20:03 UTC 2008


On Fri, 4 Apr 2008, Julian Elischer wrote:
 > Ian Smith wrote:
 > > On Thu, 3 Apr 2008, Julian Elischer wrote:
 > >
 > >  > 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!
 > 
 > yep you are right..
 > boy is that ever a broken feature..
 > there is no way to set a new session without leaping off into the
 > previously  declared sessions.
 > 
 > I just tested it and it does as you say...
 > 
 > 00001 166 17438 skipto 1000 tcp from any to 10.2.2.2 keep-state
 > 00002   9   886 allow ip from any to any
 > 01000 166 17438 count log ip from any to any
 > 01001  93  7727 count log tcp from any to 10.2.2.2
 > 01002  73  9711 count log tcp from 10.2.2.2 to any
 > 65535 166 17438 allow ip from any to any
 > 
 > I'm stunned..
 > boy is that ever a broken feature..
 > There is no way to set a new session without leaping off into the
 > previously  declared sessions.
 > 
 > I also have to check my firewalls to see if I'm hitting unexpeced 
 > behaviour.

I don't see why you think it's broken?  Apart from obvious efficiency of
having a check-state rule earlier, to get on with matching this packet
against existing dynamic rules without wading through intervening rules,
state is still only checked once; like it says, the O_PROBE_STATE opcode
only causes a state check at the first check-state, keep-state or limit
rule (encountered); any others found then become a short-path NOP.

Personally I like to do traffic accounting before any packet is whisked
off to be dealt with (and accounted by) any keep-state rules, though as
your example shows that can be done afterwards, if not piped or such.

But I can't see why you ever wouldn't want to check the existing state
of any src-addr/src-port <-> dst-addr/dst-port packet before attempting
to add a new dynamic rule for that same session?

cheers, Ian

 > > 
 > > 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
 > > 
 > > _______________________________________________
 > > freebsd-net at freebsd.org mailing list
 > > http://lists.freebsd.org/mailman/listinfo/freebsd-net
 > > To unsubscribe, send any mail to "freebsd-net-unsubscribe at freebsd.org"
 > 



More information about the freebsd-net mailing list