kern/129103: [ipfw] IPFW check state does not work =(

Ian Smith smithi at nimnet.asn.au
Thu Nov 27 22:58:06 PST 2008


Kes,

your message didn't make it to the list.  Probably because of the 650KB 
attached diagram :)  Big stuff is better put up as an URL to a web site.

On Tue, 25 Nov 2008, KES wrote:
 > #ipfw -ed show
 > 00001    0       0 check-state log
 > 00002    2     120 count log icmp from any to any via ng0
 > 00003    2     120 prob 0.500000 skipto 6 log icmp from any to any via ng0
 > 00004    0       0 skipto 5 log icmp from any to any via ng0 keep-state
 > 00005    0       0 skipto 10 log icmp from any to any via ng0
 > 00006   11     660 skipto 7 log icmp from any to any via ng0 keep-state
 > 00007    6     360 count log icmp from any to any via ng0
 > 00010    6     360 count log icmp from any to any via ng0
 > 00049  726  132682 nat 1 ip from any to any via ng0
 > 00050 9088 2196349 allow ip from any to any
 > 00050    0       0 nat 1 ip from any to any via ng0
 > 00100    0       0 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
 > 65535    0       0 deny ip from any to any
 > ## Dynamic rules (2):
 > 00006    7     420 (1s) STATE icmp 192.168.9.4 0 <-> 213.180.193.123 0
 > 00006    2     120 (1s) STATE icmp 213.180.193.123 0 <-> 92.113.76.40 0

Ok, this one is easier because no packets took the 4/5 prob split.

Note 2 separate state entries because of the different address pairs.

 > Nov 24 23:54:25 home kernel: ipfw: 2 Count ICMP:8.0 192.168.9.4 213.180.193.123 out via ng0
 > Nov 24 23:54:25 home kernel: ipfw: 3 SkipTo 6 ICMP:8.0 192.168.9.4 213.180.193.123 out via ng0
 > Nov 24 23:54:25 home kernel: ipfw: 6 SkipTo 7 ICMP:8.0 192.168.9.4 213.180.193.123 out via ng0
 > Nov 24 23:54:25 home kernel: ipfw: 7 Count ICMP:8.0 192.168.9.4 213.180.193.123 out via ng0
 > Nov 24 23:54:25 home kernel: ipfw: 10 Count ICMP:8.0 192.168.9.4 213.180.193.123 out via ng0

1st packet.  At 6 it creates state 192.168.9.4 <--> 213.180.193.123

 > Nov 24 23:54:25 home kernel: ipfw: 2 Count ICMP:0.0 213.180.193.123 92.113.76.40 in via ng0
 > Nov 24 23:54:25 home kernel: ipfw: 3 SkipTo 6 ICMP:0.0 213.180.193.123 92.113.76.40 in via ng0
 > Nov 24 23:54:25 home kernel: ipfw: 6 SkipTo 7 ICMP:0.0 213.180.193.123 92.113.76.40 in via ng0
 > Nov 24 23:54:25 home kernel: ipfw: 7 Count ICMP:0.0 213.180.193.123 92.113.76.40 in via ng0
 > Nov 24 23:54:25 home kernel: ipfw: 10 Count ICMP:0.0 213.180.193.123 92.113.76.40 in via ng0

1st response packet .. doesn't match the above state as it's a different 
address, so it creates state between 213.180.193.123 <--> 92.113.76.40 
.. because you've put your nat rule in the wrong place, it should go 
before any of this, then all state would be between 213... and 92...

Note both of the above count at rule 2, because neither match existing 
state.  These two are also the last packets that do match rule 2.

 > Nov 24 23:54:25 home kernel: ipfw: 6 SkipTo 7 ICMP:0.0 213.180.193.123 192.168.9.4 out via ng1

So now, after NAT, this matches the first state above.  It still matches 
rules 'via ng0' because that was its source interface on keep-state.  It 
doesn't show in rule 2 because it jumps to rule 6 at the check-state.

 > Nov 24 23:54:26 home kernel: ipfw: 6 SkipTo 7 ICMP:8.0 192.168.9.4 213.180.193.123 in via ng1

Second ping.  Despite coming in via ng1, it also matches the first state 
flow above, but doesn't match 7 or 10 (wrong interface)

 > Nov 24 23:54:26 home kernel: ipfw: 6 SkipTo 7 ICMP:8.0 192.168.9.4 213.180.193.123 out via ng0

Another packet out, matches established state for first rule 6 ..

 > Nov 24 23:54:26 home kernel: ipfw: 7 Count ICMP:8.0 192.168.9.4 213.180.193.123 out via ng0
 > Nov 24 23:54:26 home kernel: ipfw: 10 Count ICMP:8.0 192.168.9.4 213.180.193.123 out via ng0

.. and also matches 7 and 10, being via ng0.

 > Nov 24 23:54:26 home kernel: ipfw: 6 SkipTo 7 ICMP:0.0 213.180.193.123 92.113.76.40 in via ng0
 > Nov 24 23:54:26 home kernel: ipfw: 7 Count ICMP:0.0 213.180.193.123 92.113.76.40 in via ng0
 > Nov 24 23:54:26 home kernel: ipfw: 10 Count ICMP:0.0 213.180.193.123 92.113.76.40 in via ng0

Return packet matches second state entry on rule 6, from check-state.

 > Nov 24 23:54:26 home kernel: ipfw: 6 SkipTo 7 ICMP:0.0 213.180.193.123 192.168.9.4 out via ng1

Same packet matching first state entry on rule 6, despite interface ng1, 
after NAT.

 > Nov 24 23:54:27 home kernel: ipfw: 6 SkipTo 7 ICMP:8.0 192.168.9.4 213.180.193.123 in via ng1

Third ping packet from ng1, also matches the first of rule 6 states, but 
not counted by 7 or 10 because of via different interface.

 > Nov 24 23:54:27 home kernel: ipfw: 6 SkipTo 7 ICMP:8.0 192.168.9.4 213.180.193.123 out via ng0

Same packet going out, matches second of the rule 6 states ..

 > Nov 24 23:54:27 home kernel: ipfw: 7 Count ICMP:8.0 192.168.9.4 213.180.193.123 out via ng0
 > Nov 24 23:54:27 home kernel: ipfw: 10 Count ICMP:8.0 192.168.9.4 213.180.193.123 out via ng0

.. which also gets counted by 7 and 10, being out via ng0.

 > Nov 24 23:54:27 home kernel: ipfw: 6 SkipTo 7 ICMP:0.0 213.180.193.123 92.113.76.40 in via ng0

Return packet, matches second rule 6 state ..

 > Nov 24 23:54:27 home kernel: ipfw: 7 Count ICMP:0.0 213.180.193.123 92.113.76.40 in via ng0
 > Nov 24 23:54:27 home kernel: ipfw: 10 Count ICMP:0.0 213.180.193.123 92.113.76.40 in via ng0

.. and 7 and 10.

 > Nov 24 23:54:27 home kernel: ipfw: 6 SkipTo 7 ICMP:0.0 213.180.193.123 192.168.9.4 out via ng1

Third reply being delivered, after NAT, to ng1 because it matches state.

That's it.  If you'd had separate rules to count icmp via ng1 you'd have 
seen them all (or if you'd specifed ng* rather than ng0)

=======

 > Seems all is ok, except
 > 00001    0       0 check-state log
 > Once dynamic rule is matched, check-state counter must be increased.

No use saying 'must', when that's simply not how it works; check-state 
never shows any counts.  You've filed a PR on the basis of how you wish 
it might work, and I believe I've shown above that it's working as 
advertised.  I suggest you should now close this PR.

=======

 > Also it seems when keep-state create new dynamic rule, it must
 > increment it. Because of when rule body is matched, keep-state create
 > dynamic rule and then packet flow through this dynamic rule, counter
 > for dynamic rule is incremented, packet for parent rule is
 > incremented. and you do not loose counters.

Again, that's just not how it works.  Dynamic rule counts show traffic 
due to established state; they don't include the original packet that 
created the state.  Sometimes you'll see dynamic rule counts of 0, where 
a packet went out (say) and setup state, but no response came back.

In short, there's no use trying to use statistics from keep-state rules 
for accounting.  Use separate stateless rules, BEFORE any check-state.

 > But now NOTICE: 11 packets for rule 6 and 9 (7+2) packets for dinamic rule
 > with rule 6 as parent. 11 != 9, so here couter lost 2 packets =(

Plus the original 2 trigger packets, shown above at rule 2, making 11 
packets shown at rule 6.  I'm not entirely sure why the total of 12 
packets isn't shown (6 packets, each both in and out as you've not 
specified direction on your rules), but suspect it's to do with the 
position of your NAT rule, and/or the fact that you're not showing 
counts for 'via ng1'.
 
 > Another usefull feature is that that check-state must have body.
 > You must allow user to check same conditions as for other rules. For
 > example:
 > check-state icmp via ng0
 > It will prevent user from having unexpected maching for flows on other
 > interfaces  (ng1 interface as in my example).
 > Also I think it will be handy to have many tables for dynamic rules.
 > It is like to have many routing tables. For example:
 > ipfw add xxx check-state 1 via ng0
 > ipfw add xxx check-state 2 via ng1
 > ipfw add xxx skipto yyy icmp via ng0 keep-state 1
 > ipfw add xxx skipto yyy icmp via ng1 keep-state 2

I suggest you'll need to study the code of /usr/src/sbin/ipfw/ipfw2.c 
and /sys/netinet/ip_fw*.[ch] to find out just how it works now, before 
writing code to implement the above.  I can't see anyone else taking an 
interest in implementing such a thing (but I've been wrong before :)

 > ipfw is flexible firewall so it must remain its flexibility
 > But now ipfw has not its flexibility for check-state/keep-state because of
 > it does not allow to control what I want to check-state/keep-state

You just need to understand how ipfw stateful rules really work, and 
code your rules to match that; then I expect you will enjoy success.

cheers, Ian
(professing no more than a VERY minimal grasp of this code ..)


More information about the freebsd-ipfw mailing list