IPFW Packet tag leaks from connection setup packet (SYN) to connection refusal packet (RST)

I'm experimenting with ipfw packet tagging (tag/untag/tagged keywords) and keep getting unexpected results in seemingly trivial cases.
The configuration for this experiment is:

Bash:
# ipfw show
00100 0   0 allow ip from any to any via lo0
00200 0   0 count tag 3 in recv igb0 dst-ip 192.168.33.1 not src-port 22 not dst-port 22       # 192.168.33.1 is the IP of igb0 iface
00300 0   0 count log tagged 3
00500 0   0 allow tagged 3
00600 1 120 allow not tagged 3
65535 0   0 deny ip from any to any

My intent was to tag all non-SSH traffic from igb0 network to the localhost, so that I can use "tagged" option in some rule body later (not in this experiment). The key line is 200. It's supposed to assing tag "3" to all non-SSH packets entering the host from igb0 network. The way I understood the manual, line 200 should match the packets which satisfy all of the following criteria:
- incoming to the firewall server;
- received on interface igb0;
- addressed to 192.168.33.1, which is the inet address of igb0 interface;
- not coming from and not destined to the SSH port.

Line 300 is supposed to log all matched packets.

Then, with these rules in effect, I ran telnet 192.168.33.1 80 from a host on igb0 network. No web server was running on the firewall host, so the connection was expected to fail. It did fail. Then I've examined /var/log/security to see which packets were actually tagged. To my surprise, some packets NOT matching the above criteria showed up in the log:

Bash:
# tail -n 11 -f /var/log/security
Mar 10 10:57:15 site-03 kernel: ipfw: Accounting cleared.
Mar 10 10:57:16 site-03 kernel: ipfw: 300 Count TCP 192.168.33.6:57135 192.168.33.1:80 in via igb0      # SYN packet
Mar 10 10:57:16 site-03 kernel: ipfw: 300 Count TCP 192.168.33.1:80 192.168.33.6:57135 out via igb0     # RST packet
Mar 10 10:57:17 site-03 kernel: ipfw: 300 Count TCP 192.168.33.6:57135 192.168.33.1:80 in via igb0
Mar 10 10:57:17 site-03 kernel: ipfw: 300 Count TCP 192.168.33.1:80 192.168.33.6:57135 out via igb0
Mar 10 10:57:17 site-03 kernel: ipfw: 300 Count TCP 192.168.33.6:57135 192.168.33.1:80 in via igb0
Mar 10 10:57:17 site-03 kernel: ipfw: 300 Count TCP 192.168.33.1:80 192.168.33.6:57135 out via igb0
Mar 10 10:57:18 site-03 kernel: ipfw: 300 Count TCP 192.168.33.6:57135 192.168.33.1:80 in via igb0
Mar 10 10:57:18 site-03 kernel: ipfw: 300 Count TCP 192.168.33.1:80 192.168.33.6:57135 out via igb0
Mar 10 10:57:18 site-03 kernel: ipfw: 300 Count TCP 192.168.33.6:57135 192.168.33.1:80 in via igb0
Mar 10 10:57:18 site-03 kernel: ipfw: 300 Count TCP 192.168.33.1:80 192.168.33.6:57135 out via igb0

This log shows several attempts by telnet to establish a TCP connection. Telnet sends SYN packets to start the connection, those packets got detected and logged by the firewall rules as planned.
What's unexpected is that the log contains 'connection refused' RST packets sent by the server in reply. I mean, RST packets as such are quite expected -- they indicate that no process is listening on port 80 and that the connection is refused, but why are they logged here? I didn't expect RST packets to get tagged "3" because they match virtually none of criteria for assigning that tag: they are 'out xmit igb0' rather than 'in recv igb0', and their dst-ip is not what the rule requires.

The counters after the experiment are consistent with the log:

Bash:
# ipfw show
00100  0     0 allow ip from any to any via lo0
00200  5   260 count tag 3 in recv igb0 dst-ip 192.168.33.1 not src-port 22 not dst-port 22
00300 10   460 count log tagged 3
00500 10   460 allow tagged 3
00600 51 13224 allow not tagged 3          # SSH traffic. I ran the test over SSH.
65535  0     0 deny ip from any to any

The counters confirm that only 5 SYN packets matched rule 200, which is good. The counters also confirm that 10 packets were logged, which is bad. How did the other 5 packets (RST ones) get that tag? No rule other than 200 could have assigned it.

To clarify the things a bit, I ran another ruleset, where 'log' is the first rule:

Bash:
# ipfw show
00050  5   200 count log tagged 3
00100  0     0 allow ip from any to any via lo0
00200  5   260 count tag 3 in recv igb0 dst-ip 192.168.33.1 not src-port 22 not dst-port 22
00300 10   460 count tagged 3
00500 10   460 allow tagged 3
00600 45 11800 allow not tagged 3         # SSH traffic. I ran the test over SSH.
65535  0     0 deny ip from any to any

So it seems that RST packets are already tagged "3" on entering the firewall. That explains the outcome a bit, but the question still remains: How did they get tagged?
The manual says that tags are "sticky", and once applied they stay with the packet until it leaves the kernel. But SYN and RST are different packets, right? They are related semantically, but physically they are separate. Tags shouldn't implicitly propagate from one packet to another one, right? Or should they? Is it documented/explained somewhere?
 
Back
Top