Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 04 Apr 2008 10:40:10 -0700
From:      Julian Elischer <julian@elischer.org>
To:        Ian Smith <smithi@nimnet.asn.au>
Cc:        freebsd-net@freebsd.org, Ivan Voras <ivoras@freebsd.org>
Subject:   Re: Trouble with IPFW or TCP?
Message-ID:  <47F667FA.6020208@elischer.org>
In-Reply-To: <Pine.BSF.3.96.1080405010904.6611B-100000@gaia.nimnet.asn.au>
References:  <Pine.BSF.3.96.1080405010904.6611B-100000@gaia.nimnet.asn.au>

next in thread | previous in thread | raw e-mail | index | archive | help
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.

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




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?47F667FA.6020208>