Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 21 Aug 2006 11:23:50 +0200
From:      Daniel Hartmeier <daniel@benzedrine.cx>
To:        Rostislav Krasny <rosti.bsd@gmail.com>
Cc:        freebsd-net@freebsd.org
Subject:   Re: PF or "traceroute -e -P TCP" bug?
Message-ID:  <20060821092350.GL20788@insomnia.benzedrine.cx>
In-Reply-To: <20060818235756.25f72db4.rosti.bsd@gmail.com>
References:  <20060818235756.25f72db4.rosti.bsd@gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help
[ I'm CC'ing Crist, maybe he can explain why -e behaves like it does ]

On Fri, Aug 18, 2006 at 11:57:56PM +0300, Rostislav Krasny wrote:

> I've tried the new "-e" traceroute option on today's RELENG_6 and
> found following problem:
> 
> > traceroute -nq 1 -e -P TCP -p 80 216.136.204.117

As I understand the -e option, that should send a sequence of TCP SYNs
with

  - constant source port (randomly picked per invokation)
  - constant destination port 80
  - increasing TTL per probe

Assuming you pass the packets with pf, it matters whether you create
state or not. Filtering statelessly (without 'keep state'), there should
be no problem at all. I assume you're filtering statefully.

With constant source and destination ports, the first probe should
create a state entry and all further probes (of the same traceroute
invokation) should match that state entry.

What you changed in your patch is switching to a sequential (instead of
constant) source port. This forces creation of one state per probe,
treating each probe as a separate connection. I don't think that's in
the spirit of the -e option. There's really no need for that, once the
underlying problem is fixed.

So, why doesn't -e without your patch produce probes that all match a
single state entry?

Look at how the TCP sequence numbers are generated across the probes:

	tcp->th_seq = (tcp->th_sport << 16) | (tcp->th_dport +
	    (fixedPort ? outdata->seq : 0));

This is the problem. traceroute increments the sequence number with each
probe. I don't know why that is done. Why not use the same th_seq for
all probes, like an ISN (initial sequence number) would be re-used in
retransmissions in a real TCP handshake?

If you create state on the first TCP SYN pf sees, pf will note the ISN
from the traceroute side. When pf sees further SYNs from that side, it
will deal with them like with any client retransmitting the SYN of the
handshake (before the peer replies with a SYN+ACK, giving its side's
ISN). Subsequent TCP SYNs with different ISN matching the address/port
pairs will be blocked by pf.

If this happens on the IP forwarding path (i.e. pf blocks the packet
outgoing), the stack produces the ICMP host unreachable error that shows
up as "!H" in traceroute. I assume you have a "pass out on $ext_if keep
state" rule, and don't filter on the internal interface. If you add
stateful filtering on the internal interface, I think you'll find that
subsequent TCP SYNs are blocked without eliciting the ICMP error.

I suggest traceroute with -e uses fixed th_seq, as in

-	tcp->th_seq = (tcp->th_sport << 16) | (tcp->th_dport +
-	   (fixedPort ? outdata->seq : 0));
+	tcp->th_seq = (tcp->th_sport << 16) tcp->th_dport;

Maybe the (fixedPort?:) operands were mistakenly switched, and you want to
increment th_seq when -e is NOT used, but I can't think off-hand why you
would.

Daniel



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20060821092350.GL20788>