[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Tagging didn't work as expected



On Wed, Jan 26, 2005 at 12:49:13PM -0500, Peter Fraser wrote:
> Daniel Hartmeier [[email protected]] wrote that my use of tagging 
> should work. So I moved the tagging rules to the very top of my rule set
> and did a traceroute from a different machine . This is the result
I think you didn't mention traceroute before. If you retry with ping(8)
instead, you'll see that it works. Here's why.
When you use traceroute, the sender will start with an ICMP echo request
with TTL 1, which elicits an ICMP time exceeded error from the nearest
router (an IP forwarder who decrements TTL). It repeats this with
increasing TTL values. The chronological order of ICMP time exceeded
errors produces the list of hops, from nearest to farest.
The first ICMP echo request pf will see coming in will have TTL 1 when
it reaches the pf box. Your rule @2 will match, tag the packet with tag
icmp, create state, and pass the packet.
The IP forwarding code in the kernel will, however, not forward that
first packet, since after decrementing, the TTL will be 0. Instead, it
produces the ICMP time exceeded error.
The traceroute machine will print the pf box as a hop on the path, and
send the next ICMP echo request with a TTL one higher than the last one.
This second packet arrives in on the pf box, matches the state previously
created, and gets passed due to the state entry, _without_ any ruleset
evaluations.
The important detail is that this second packet will not get tagged. In
stateful filtering, only the first packet creating the state entry will
cause tagging. Packets matching this state later on will not get tagged.
This is the reason why the man page mentions:
  pass rules that use the tag keyword must also use keep state, modulate
  state or synproxy state.
The reason for this limitation is merely performance. You generally
don't want to tag further packets of a connection, once it has created
state entries. In most cases, adding tags to packets matching state
entries would just be a waste of CPU cycles.
This second ICMP echo request has a higher TTL, so it doesn't become 0
after decrement, and gets forwarded. This is the first packet that
actually gets sent out on the second interface on the pf box.
There is no state entry on that interface yet, so the ruleset is
evaluated. The 'tagged icmp' rule does not match because this second
packet was never tagged!
If you use ping(8) instead of traceroute, the first packet that creates
state on the first interface will also be the one that gets filtered
first on the second interface. It will have the tag and match the
'tagged' rule. Once states exist on both interface, packets related to
this ping(8) invokation will pass matching these states, not evaluating
any more rules.
If you'd use a bridge instead of an IP forwarder, this issue wouldn't
come up, as bridges do not decrement TTL when forwarding frames.
One thing you can do to address the issue on an IP forwarder is to use
scrub's 'min-ttl' option. For instance, you can have all TTL values of
incoming packets increased from 1 to 2. This ensures that no packet will
be dropped by the IP forwarding code due to TTL reaching 0. It has the
side-effect that the pf machine will no longer produce ICMP time
exceeded errors, and therefore won't show up in traceroute output as a
hop. Some people use the 'min-ttl' option precisely because of that
effect, to hide the hop. In your case, it might be just an unwelcome
side-effect.
Nice puzzle, took me half an hour to find the explanation :)
Daniel