[comp.protocols.tcp-ip] 4.X implementations of TCP, initial sequence numbers, and windows

rwolski@lll-lcc.UUCP (Richard Wolski) (10/26/89)

Hello everyone.

I have a BSD implementation question regarding the initial sequence
number and the advertised window.  In some of the BSD code that we have
(vendors will remain nameless to protect the innocent) the following
piece of code appears:

if(win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
	tp->rcv_adv = tp->rcv_nxt + win;

What we notice is that another vendor's implementation of TCP chooses an
initial sending sequence number with the sign bit set (sometimes) and that
the rcv_adv field in the tcpcb always remains 0.  I think the following thing
is happening.  SEQ_GT is defined as:

#define SEQ_GT(a,b)  ((int)((a)-(b)) > 0)

When expanded in the above code, (a) has the sign bit set, (b) is zero, so the
test fails and rcv_adv never gets set properly.  This manifests itself
as unusually large windows where one would not expect them.

My first question:  Am I reading this right?  I checked the code to set the
iss on the sending side, and it periodically increments a global variable
which eventually results in a negative number (when viewed as an int).

But wait, there's more...

We looked further at the sender's code for setting iss and saw the following
statements:

#ifdef TCP_COMPAT_42
	if((int)tcp_iss < 0)
		tcp_iss = 0;			/* XXX */
#endif

This makes me believe that the problem is somehow fixed at 4.3.  Is that
true?  We looked at yet another vendor's implementation which was supposed
to be 4.3, and nothing seems to be different. 

Any thoughts that you might have in this matter will be gratefully appreciated,
and I apologize if I am asking for the answer to a question that everybody
except me is privy to.

Rich Wolski
rwolski@lll-lcc.llnl.gov		inter-net
(415)423-8594				bell-net
P.O. Box 808, L-60			mail-net
Livermore, CA  94538

srg@quick.COM (Spencer Garrett) (10/29/89)

In article <2642@lll-lcc.UUCP>, rwolski@lll-lcc.UUCP (Richard Wolski) writes:
> 
> if(win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
> 	tp->rcv_adv = tp->rcv_nxt + win;
> 
> What we notice is that another vendor's implementation of TCP chooses an
> initial sending sequence number with the sign bit set (sometimes) and that
> the rcv_adv field in the tcpcb always remains 0.  I think the following thing
> is happening.  SEQ_GT is defined as:
> 
> #define SEQ_GT(a,b)  ((int)((a)-(b)) > 0)
> 
> When expanded in the above code, (a) has the sign bit set, (b) is zero, so the
> test fails and rcv_adv never gets set properly.  This manifests itself
> as unusually large windows where one would not expect them.
> 
> My first question:  Am I reading this right?  I checked the code to set the
> iss on the sending side, and it periodically increments a global variable
> which eventually results in a negative number (when viewed as an int).

This is the key.  Sequence numbers are unsigned values which must be
interpreted modulo 2**32.  Thus, the sequence number following
0xffffffff is 0, and the difference of these two is 1.  On a machine
with 32 bit ints this can be accomplished by subtracting the two
sequence numbers, ignoring overflow, and casting the result to a signed
integer.  If the int's on your machine aren't exactly 32 bits long,
however, this algorithm won't work correctly.

My guess is that your code isn't initializing tp->rcv_adv during
TCP startup.  You should be setting rcv_nxt from the initial sequence
number you get from the peer host in the SYN packet (plus 1 for the
SYN), and you should initialize rcv_adv to rcv_nxt plus your initial
offered window.  The code you've quoted above only sets rcv_adv
when it thinks it's extending the window.  If rcv_adv is zero and
rcv_nxt has its "sign" bit set, but isn't close to the top of the
range, then the window looks huge, so your code never thinks it
should be extended.  If the sign bit were clear, however, then
the window would appear negative, and your code would happily reset
it the first time through.