Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 26 May 2018 18:03:49 -0700
From:      Jeff <freebsd@wagsky.com>
To:        freebsd-ipfw@freebsd.org
Subject:   Unexpected behavior ipfw check-state with count tag or call
Message-ID:  <583c0634-c87c-5502-300b-6450253f71a7@wagsky.com>

next in thread | raw e-mail | index | archive | help
TL;DR

If an ipfw rule's action is "count [tag]" or "call" and initiates a
keep-state, when the check-state is matched, the execution not only
performs the action of the original rule, but also the rule
number. This results in the "continuation" being not where the
check-state was executed but where the corresponding keep-state is
numbered.

Before I either file a ticket or start to work on the code or docs,
is this intended behavior?

---

Context:

IPv4 and NAT -- Capture the "established" connections on egress by use
of a "count tag XXX keep-state :out-ifN" rule. When a "return" packet
arrives, the "check-state :out-ifN" would then tag the packet XXX,
which could be used in identifying it as it flows through the firewall
as "expected from valid connection", even as its addr:port is modified.
You can't "accept keep-state" as the packet still needs NAT on the way in.

I've tried "count tag" as well as "call" to "tag ; return" with the same
results, the "next" rule evaluated is the one following the keep-state
rule, not the check-state rule.

(I've still got a couple other things I'm going to try as work-arounds.)


Once I've got my rules running here, I'd like to tackle this, but I
don't know if this is a "documentation weakness", or perhaps
unintended behavior, based on the man pages and what I've been able to
find in the code. I know the man page and comments have grown
organically since I started using ipfw back in the 4.0 days and may
not be completely up-to-date.

If there's any insight into intent of the code before I dive in, I'd
certainly appreciate it!

Thanks,

Jeff





ipfw(8):

      check-state [:flowname | :any]
              Checks the packet against the dynamic ruleset.  If a match is
              found, execute the action associated with the rule which
              generated this dynamic rule, otherwise move to the next rule.

"action associated with the rule" -- not the rule itself



ip_fw_dynamic.c has some "interesting" comments:

  * Each dynamic rule holds a pointer to the parent ipfw rule so
  * we know what action to perform. Dynamic rules are removed when
  * the parent rule is deleted. This can be changed by dyn_keep_states
  * sysctl.
  *
  * There are some limitations with dynamic rules -- we do not
  * obey the 'randomized match', and we do not do multiple
  * passes through the firewall. XXX check the latter!!!



ip_fw2.c appears to the match/dispatch:

2191 /*
2192  * Found dynamic entry, update stats
2193  * and jump to the 'action' part of
2194  * the parent rule by setting
2195  * f, cmd, l and clearing cmdlen.
2196  */

(I haven't pursued the code deeper yet)




Here are the rules used for confirming this behavior


192.168.0.100 is the management interface
10.0.0.100 is the "outside" interface, leading to the "true" gateway
172.217.11.164 is an IP of www.google.com used to drive outside traffic

00001 reass
00010 allow via lo0
00011 deny ip from 127.0.0.0/8 to any
00012 deny ip from any to 127.0.0.0/8
00013 deny ip from ::1 to any
00014 deny ip from any to ::1
00020 allow tcp from 192.168.0.100 22 to 192.168.0.100/24 via igb2
00021 allow tcp from 192.168.0.100/24 to 192.168.0.100 dst-port 22 via igb2
00080 skipto 90 log proto tcp src-ip 10.0.0.100 via igb0 dst-ip 
172.217.11.164 dst-port 443
00081 skipto 90 log proto tcp src-ip 172.217.11.164 src-port 443 dst-ip 
10.0.0.100 via igb0
00092 skipto 2000 log not layer2 in
00093 skipto 3000 log not layer2 out recv *
00094 skipto 4000 log not layer2 out // not recv *
00099 deny log // first-stage dispatch problem
02000 count log // ip_input
02100 count log tagged 40
02104 check-state :IP4_TAG_OUTER_outside log // "established" IPv4 
connections
02500 count log // Inbound "NAT" of IPv4
02510 allow log tagged 40 // "established" IPv4 connections,post-NAT
02999 deny log // ip_input -- DENY remaining
03000 deny log // ip_output -- forwarded
04000 count log // ip_output -- common output
04204 count log // Outbound "NAT"
04304 count log tag 40 xmit igb0 keep-state :IP4_TAG_OUTER_outside
04400 allow log tagged 40
04999 deny log // ip_output -- common output -- DENY remaining
65535 deny ip from any to any

# First packet goes out, tagged keep-state at 4304, accepted at 4400

ipfw: 80 SkipTo 90 TCP 10.0.0.100:45427 172.217.11.164:443 out via igb0
ipfw: 94 SkipTo 4000 TCP 10.0.0.100:45427 172.217.11.164:443 out via igb0
ipfw: 4000 Count TCP 10.0.0.100:45427 172.217.11.164:443 out via igb0
ipfw: 4204 Count TCP 10.0.0.100:45427 172.217.11.164:443 out via igb0
ipfw: 4304 Count TCP 10.0.0.100:45427 172.217.11.164:443 out via igb0
ipfw: 4400 Accept TCP 10.0.0.100:45427 172.217.11.164:443 out via igb0

# Return packet comes back, check-state fires at 2104 (as expected)
# Action is logged as 4304 (location of the keep-state rule)
# Then continues at 4400 -- UNEXPECTED

ipfw: 81 SkipTo 90 TCP 172.217.11.164:443 10.0.0.100:45427 in via igb0
ipfw: 92 SkipTo 2000 TCP 172.217.11.164:443 10.0.0.100:45427 in via igb0
ipfw: 2000 Count TCP 172.217.11.164:443 10.0.0.100:45427 in via igb0
ipfw: 2104 UNKNOWN TCP 172.217.11.164:443 10.0.0.100:45427 in via igb0
ipfw: 4304 Count TCP 172.217.11.164:443 10.0.0.100:45427 in via igb0
ipfw: 4400 Accept TCP 172.217.11.164:443 10.0.0.100:45427 in via igb0

[...]




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?583c0634-c87c-5502-300b-6450253f71a7>