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