Bug 60889

Summary: 5.2RC2 - zero IP id change not effective for TCP, detrimental to security/privacy and maybe interoperation
Product: Base System Reporter: Richard Wendland <richard>
Component: kernAssignee: Andre Oppermann <andre>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: Unspecified   
Hardware: Any   
OS: Any   

Description Richard Wendland 2004-01-04 12:10:21 UTC
The src/sys/netinet/ip_output.c Revision 1.189 change to IP to generate a
zero ip_id field when DF is used only partly achieves its objectives for
TCP, and is (arguably) a detriment to security/privacy.  There is also
a risk of TCP interoperation problems with some Windows95/2000 systems
using VJ header compression from this change.  This change seems to have
taken place after release 5.1, so is new to 5.2 RC1 and RC2.

TCP emits FIN-ACK without DF, so now just one segment per TCP connection
goes out with a non-zero ip_id.  This means the change's objectives are
not really met for TCP; it just means FIN-ACK must be observed.

Worse, for many systems the ip_id now becomes a close approximate count
of the number of TCP connections it has made, so external observers
can fairly easily count the number of TCP connections a system makes,
which was not possible before this change.  To me this seems more of
a privacy issue for all FreeBSD users than the NAT-only issue in Steve
Bellovin's paper this change seeks to solve.

Another consideration is that Linux made exactly this change some time
ago, around Linux 2.4.4, but was forced to backout the change because
(I think) of practical connectivity issues related to the comment in
include/net/ip.h ip_select_ident() where it now implements an incrementing
ip_id for DF:

    /* This is only to work around buggy Windows95/2000
     * VJ compression implementations.  If the ID field
     * does not change, they drop every other packet in
     * a TCP stream using header compression.
     */

Has anyone checked that this buggy Windows95/2000 VJ compression problem
is no longer an issue in practice?  I doubt that the large web hosters
who use FreeBSD would be best-pleased if they ran into this for even
just a few users - especially as there is no config option to disable
this change.

I suggest this change is backed out of 5.2, to give more time for these
issues to be considered and tested.

NB I've read the source and I can't see why the FIN-ACK goes out without
a DF, it's emitted as TCP moves from FIN_WAIT_x state to TIME_WAIT
probably at line 3091 of tcp_input.c with a call to tcp_output().
tcp_output() always appears to set IP_DF at line 998 of tcp_output.c,
if path_mtu_discovery is enabled.  A puzzle.

Here's tcpdump output from a HTTP HEAD request showing the issue,
the last segment is the FIN-ACK with the only non-zero id (and no DF)
from fbsd-5_2RC2.80:

17:10:26.566423 fbsd-4_3.3700 > fbsd-5_2RC2.80: S 3955049829:3955049829(0) win 16384 <mss 1460,nop,wscale 0,nop,nop,timestamp 1986916345 0> (DF) (ttl 64, id 36227)
17:10:26.604219 fbsd-5_2RC2.80 > fbsd-4_3.3700: S 2579960593:2579960593(0) ack 3955049830 win 65535 <mss 1452,nop,wscale 1,nop,nop,timestamp 16007425 1986916345> (DF) (ttl 53, id 0)
17:10:26.604259 fbsd-4_3.3700 > fbsd-5_2RC2.80: . ack 1 win 17280 <nop,nop,timestamp 1986916349 16007425> (DF) (ttl 64, id 36228)
17:10:26.604426 fbsd-4_3.3700 > fbsd-5_2RC2.80: P 1:61(60) ack 1 win 17280 <nop,nop,timestamp 1986916349 16007425> (DF) (ttl 64, id 36229)
17:10:26.655004 fbsd-5_2RC2.80 > fbsd-4_3.3700: P 1:278(277) ack 61 win 33120 <nop,nop,timestamp 16007429 1986916349> (DF) (ttl 53, id 0)
17:10:26.659403 fbsd-5_2RC2.80 > fbsd-4_3.3700: F 278:278(0) ack 61 win 33120 <nop,nop,timestamp 16007429 1986916349> (DF) (ttl 53, id 0)
17:10:26.659430 fbsd-4_3.3700 > fbsd-5_2RC2.80: . ack 279 win 17280 <nop,nop,timestamp 1986916354 16007429> (DF) (ttl 64, id 36231)
17:10:26.659468 fbsd-4_3.3700 > fbsd-5_2RC2.80: F 61:61(0) ack 279 win 17280 <nop,nop,timestamp 1986916354 16007429> (DF) (ttl 64, id 36232)
17:10:26.694533 fbsd-5_2RC2.80 > fbsd-4_3.3700: . ack 62 win 33119 <nop,nop,timestamp 16007434 1986916354> (ttl 53, id 25945)

Note that with this change ip_id is no longer in network byte order,
so id appears to increase in steps of 256 from i386 systems; you need
to be aware of this if you are using this to count TCP connections.

Incidentally this is also a new information leak from this change, you
can remotely identify the endian-ness of FreeBSD systems, in practice
currently if it runs on i386; whether this really matters for security
is debatable.

Thanks to Boris Staeblow <bs@dva.in-berlin.de> and Tim Rylance
<tkr@puffball.demon.co.uk> for highlighting this change and helping me
diagnose the issues.

Fix: 

Back out the src/sys/netinet/ip_output.c Revision 1.189 change:

wollman     2003/05/31 10:55:21 PDT

FreeBSD src repository

Modified files:
  sys/netinet          ip_output.c
Log:
Don't generate an ip_id for packets with the DF bit set; ip_id is
only meaningful for fragments.  Also don't bother to byte-swap the
ip_id when we do generate it; it is only used at the receiver as a 
nonce.  I tried several different permutations of this code with no
measurable difference to each other or to the unmodified version, so
I've settled on the one for which gcc seems to generate the best code.
(If anyone cares to microoptimize this differently for an architecture
where it actually matters, feel free.)

Suggested by:   Steve Bellovin's paper in IMW'02

Revision  Changes    Path
1.189     +17 -4     src/sys/netinet/ip_output.c
How-To-Repeat: Make TCP connections to a 5.2-RC2 system.  Use tcpdump to
see the issue, observe the final FIN-ACK of a connection.
Comment 1 matusita freebsd_committer freebsd_triage 2004-01-05 12:58:43 UTC
Responsible Changed
From-To: freebsd-bugs->wollman

Wollman, the originator says that the commit mentioned in this PR, 
was made by you.  Would you please take care of this?
Comment 2 Garrett Wollman freebsd_committer freebsd_triage 2004-01-05 17:44:48 UTC
Responsible Changed
From-To: wollman->freebsd-bugs

Put this back in the pool.  I have absolutely zero time to work on this 
right now.  It may be easier to simply back out the change until the reason 
for the odd behavior on FIN segments is identified.
Comment 3 richard 2004-01-08 10:59:32 UTC
I have identified a further problem with this change:

This change causes ip_id for non-DF to be output in native byte order in
ip_output.c.  Unfortunately ip_id is still output in Network Byte Order
in ip_mroute.c and raw_ip.c, so this change risks little-endian machines
emitting the same IP fragmentation id at about the same time from these
different modules (after another 255 packets), rather than after the usual
64k cycle; creating a small but real risk of fragment re-assembly errors.

	Richard
-- 
Richard Wendland				richard@wendland.org.uk
Comment 4 Andre Oppermann freebsd_committer freebsd_triage 2004-01-09 12:53:35 UTC
State Changed
From-To: open->analyzed

We have analyzed the problem and a patch has been committed to 
-current. 


Comment 5 Andre Oppermann freebsd_committer freebsd_triage 2004-01-09 12:53:35 UTC
Responsible Changed
From-To: freebsd-bugs->andre

Take over.
Comment 6 Andre Oppermann freebsd_committer freebsd_triage 2004-01-14 15:18:24 UTC
State Changed
From-To: analyzed->patched

Fixed in netinet/ip_output.c rev 1.204:  Do not set ip_id to zero 
when DF is set on packet and restore the general pre-randomid behaviour. 

Fixed in netinet/tcp_subr.c rev 1.173:  If path mtu discovery is 
enabled set the DF bit in all cases we send packets on a tcp connection.
Comment 7 Andre Oppermann freebsd_committer freebsd_triage 2004-08-27 19:45:45 UTC
State Changed
From-To: patched->closed

Work is done.