Subject: Send window update algorithm ...

From: Ramesh Shankar <[email-masked]>
Date: Tue, 27 Jun 2000 12:31:29 -0600

[I apologise for sending this message again. But my previous attempt
resulted in an incomplete message being sent out by the screwed up
Netscape e-mail that I used].

--------------------
Here is what I understood about a correct implementation of a send
window update algorithm (This algorithm is responsible for deciding
whether or not the SEG.WND information in an incoming segment can be
used to update the SND.WND information):

1. Information from older segments shouldn't be used. (SEG.ACK < SND.UNA
or SEG.ACK > SND.MAX). It is assumed that the received segment has been
trimmed to fit in the receive window already.
2. Out of order segments shouldn't end up shrinking the window,
especially out of order plain ACKs (see RFC 793, page 43).

Here is one which I add to the above list:

3. With bidirectional data transfer, we could have updated the window
using an out of order segment. If the peer times out and retransmits (or
if we got a fast retransmit), we will start getting sequence #s which
are less than SND.WL1. As long as these segments ACK newer data or
advertise a bigger window, we should use SEG.WND to update SND.WND.

The standard BSD algorithm below, doesn't handle case 3 mentioned above:

Use SEG.WND to update SND.WND if:

- if (SEG.SEQ > SND.WL1)
- OR if ((SEG.SEQ == SND.WL1) && (SEG.ACK > SND.WL2))
- OR if ((SEG.ACK == SND.WL2)) && (SEG.WND > SND.WND))

In fact, during my testing, I ran exactly into case 3 mentioned above.
Instead of using the window information from a retransmission by the
peer which ACKed new data that we sent and which made the window 0, we
ignored the SEG.WND information (as SEG.SEQ < SND.WL1) and continued to
think that we had a non-zero window and tried to send another segment.
To the code this appeared as if the window had shrunk.

I am not sure why the following algorithm can't be used, which handles
all the cases mentioned above:

Use SEG.WND to update SND.WND if:

- if (SEG.SEQ > SND.WL1) [Set SND.WL1 = SEG.SEQ]
- OR if (SEG.ACK > SND.WL2) [Set SND.WL2 = SEG.ACK]
- OR if ((SEG.SEQ == SND.WL1) && (SEG.ACK == SND.WL2) && 
	(SEG.WND > SND.WND)) [Don't update either SND.WL1 or SND.WL2].

Any insight would be greatly apprecited.

Thanks,

S.R.

---

From: [email-masked]
Date: Wed, 28 Jun 2000 20:56:03 +0400 (MSK DST)

Hello!

> - OR if (SEG.ACK > SND.WL2) [Set SND.WL2 = SEG.ACK]

The same problem is present in Linux tcp implementation.

This idea was the first, which came to my head.
But then I understood that it is highly dubious.
Look, with this change SND.UNA==SND.WL2 _identically_ and
can be simple omitted from TCB state. Right? 8)8)
So, the idea is suspiciously good. 8)

To all that I understood, RFC791 window update algorithm,
ignoring window updates with SEG.SEQ < SND.WL1, is designed
in assumption that receiver MAY _shrink_ window deliberately.
In this case check SEG.SEQ >= SND.WL1 prevents spurious window
reopening.

Essentially, TCP is designed to deliver reliably
not only data, but also window updates. Certainly,
it is useful only if receiver is allowed to shrink window,
so that its status is marginal. Maybe, RFC is even wrong.
I would prefer that it were wrong. 8)

So. The thing, which I proposed to make in this situation is:

1. To leave RFC algorithm intact, as checked and proven by time.

2. But in the case, when an ACK advances SND.UNA, but
   window update from it is ignored by RFC rules,
   hold right edge of window untouched.
   I.e. reduce SND.WND by SEG.ACK-oldSND.UNA.

This change is more conservative.

Note that it works exactly as with your change in the situation
of lockup, described by you. But it is more robust, because
it rejects spurious window inflation, when receiver really tries
to shrink window.

Alexey Kuznetsov

---

From: Ramesh Shankar <[email-masked]>
Date: Wed, 28 Jun 2000 12:33:53 -0600

> The same problem is present in Linux tcp implementation.

The last time I looked at Linux, it seemed to have a totally different
set of problems and was different from the BSD implementation. I don't
recall anything further though.

> So, the idea is suspiciously good. 8)

Strictly speaking, SND.WL2 looks redundant even in the existing
implementation. SEG.ACK has to be >= SND.UNA. Otherwise, this is an "old
ACK" and we ignore it as per RFC 793, page 72. Also SEG.ACK has to be <=
SND.NXT. If an ACK is valid AND ACKs new data, SND.UNA would anyway get
updated. And TCP ACKs are cumulative. That is, one could as well check
SEG.ACK against SND.UNA instead of SND.WL2!  I don't know how SND.WL2
can legally be > SND.UNA :-)). i.e. we used the window information from
the ACK, but didn't use the ACK itself. 

It appears that the SND.WL2 use is directly based on the comment on page
43 of RFC 793. Since TCP ACKs are cumulative, it seems pretty trivial to
detect out of order ACK segments - an out of order ACK won't pass the
SEG.ACK >= SND.UNA check on RFC 793, page 72.

Okay may be I am missing something seriously here :-)).

> In this case check SEG.SEQ >= SND.WL1 prevents spurious window

Come to think of it, the intention of SEG.SEQ > SND.WL1 check is not
really that clear. A receiver can shrink the window using this loophole.
Perhaps it was intended that way: If new data is being sent or new data
is being ACKed (by the receiver), blindly use the window information
provided by the receiver.

Note, however, that the only illegal window shrinking case is that of 
pure ACKs containing window updates which shrink the window (i.e. they
didn't get out of order, they were actually sent that way). As the
current algorithms stand, one can shrink the window in a segment which
has SEG.SEQ > SND.WL1 or which has SEG.ACK > SND.WL2 (eventhough this is
strongly discouraged by RFC 1122).

The original RFC 793 algorithm didn't handle the case of pure window
updates coming out of order. (i.e. SEG.SEQ == SND.WL1, SEG.ACK ==
SND.WL2, SEG.WND specifies the updated window). This has been fixed in
the BSD algorithm. Hence the check:

if ((SEG.ACK == SND.WL2)) && (SEG.WND > SND.WND))

Perhaps any of the original TCP implementors/RFC 793 writers can shed
some light on this.

Thanks,

S.R.

---

From: [email-masked]
Date: Wed, 28 Jun 2000 23:12:32 +0400 (MSK DST)

Hello!

> set of problems and was different from the BSD implementation.

They coincide in this place literally.

> the ACK, but didn't use the ACK itself. 

SND.UNA cannot be less than SND.WL2, certainly.

But it can be greater, when window update is not accepted due
to the first rule.

In this case SND.WL2 is frozen before SND.UNA and the first duplicate
ACK with SEG.ACK==SND.UNA will change window. See?
Normally, dupacks only increase window. But dupack with SEG.SEQ > SND.WL1
is allowed to shrink it.

> strongly discouraged by RFC 1122).

Unfortunately, it was not enough strongly discouraged to force people
working on wireless devices to forget about idea to use this hole. 8)8)
Look at rfc2757.

> the BSD algorithm.

RFC793 forgot not only this. The case of SEG.ACK==SND.UNA
is completely forgotten on that page. 8)

Yes, I think all this chapter is one big bug yet.

Probably, correct approach prioritizes ACK and SEQ updates
in backward order, so that check should look like:

if (SEG.ACK > SND.UNA ||
    (SEG.ACK == SND.UNA &&
     (SEG.SEQ > SND.WL || (SEG.SEQ == SND.WL && SEG.WIN > SND.WND)))) {
	SND.WND = SEG.WND;
	SND.WL = SEG.WL;
}

Alexey Kuznetsov

---

From: Ramesh Shankar <[email-masked]>
Date: Wed, 28 Jun 2000 13:35:11 -0600

> Normally, dupacks only increase window. But dupack with SEG.SEQ > SND.WL1

I am not sure what you mean by "not accepted due to the first rule". In
the standard BSD scheme, the only case where new data is ACKed AND the
window information is NOT used is when SEG.SEQ <  SND.WL1: As I pointed,
out this causes grief during retransmission involving bidirectional data
transfer. In fact, it would appear to me that SND.WL2 was probably
originally intended to freeze the window updates during retransmits (by
the receiver) when doing bidirectional data transfer. i.e. it is more of
a "feature" than a bug. If it really was the case, I am not sure what
the motivation behind this was.

I am not able to see any other case where SND.WL2 is not moved along
with SND.UNA. Thus, if it is okay to update the send window (I don't see
any reason why we shouldn't be) during retransmits by the receiver,
SND.WL2 naturally becomes redundant. 

From a coding perspective, I can understand why SND.WL2 may have been
there: by the time you get to the send window update part, your SND.UNA
has already been set to SEG.ACK. Hence, you can't compare SND.UNA with
SEG.ACK. SND.WL2 in this case really serves as value of SND.UNA *before*
it was set to SEG.ACK.

Thanks,

S.R.

---

From: [email-masked]
Date: Thu, 29 Jun 2000 21:01:47 +0400 (MSK DST)

Hello!

> I am not sure what you mean by "not accepted due to the first rule".

Seems, I understand source of misunderstanding. 8)

That guy, who fixed RFC algorithm old days, forgot to add one level
of parenthesis in hurry. 8)

	if (((tiflags & TH_ACK) && SEQ_LT(tp->snd_wl1, ti->ti_seq)) ||
	    (tp->snd_wl1 == ti->ti_seq && (SEQ_LT(tp->snd_wl2, ti->ti_ack)) ||
					  ^
	    (tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd))) {
							       ^

Well, accepting window update with arbitrary seq _only_ from dupacks,
but ignoring it for real good acks is evident misprint. 8)

It does not change anything but adding more mess.

So, do you see something wrong in:

	if ((tiflags & TH_ACK) &&
	     (SEQ_LT(tp->snd_una, ti->ti_ack) ||
	      SEQ_LT(tp->snd_wl1, ti->ti_seq) ||
	      (tp->snd_wl1==ti->ti_seq && tiwin > tp->snd_wnd)))

I.e. 

1. new ACK always updates window.
2. dupack updates window, if it advances SND.WL1,
   or its SEG.SEQ==SND.WL1, but advertised window increases.

SND.WL2 is not used, it is ==SND.UNA.

Alexey

---

From: Ramesh Shankar <[email-masked]>
Date: Thu, 29 Jun 2000 17:34:43 -0600

The case of pure ACK segments (aka window updates) coming out of order
is not handled by the last check.

Here is the one that I quoted:

Set SND.WND = SEG.WND: [Assuming Window scaling is correctly handled].
if (SEG.SEQ > SND.WL1) [Set SND.WL1 = SEG.SEQ]
OR if (SEG.ACK > SND.WL2) [Set SND.WL2 = SEG.WL2]
OR if ((SND.WL2 == SEG.ACK) && (SEG.WND > SND.WND))

It is assumed that the segment has been already trimmed to be inside the
window and the ACK satisfies the check:

SEG.ACK <= SND.UNA <= SND.MAX.

I still retain SND.WL2 as a "local variable" which contains the previous
value of SND.UNA. [When these checks are done, SND.UNA would have been
already set to SEG.ACK. You could save it in a local variable before the
update and use it here. Doesn't matter].

The above correctly handles the following cases:

- Pure ACKs (aka window updates) coming out of order. These will have
same SEG.SEQ, SEG.ACK and (legally) increasing window sizes.
- ACK information sent in retransmitted segments. These will have
SEG.SEQ < SND.WL1, SEG.ACK >= SND.WL2.
- Window can be shrunk (even if strongly discouraged by RFC 1122) with
SEG.SEQ > SND.WL1, SEG.ACK > SND.WL2, but NOT otherwise.
- The case of segments coming out of order is anyway automatically
handled as such a segment will have SEG.ACK <= SND.WL2.

Thanks,

S.R.

---

From: Kacheong Poon <[email-masked]>
Date: Thu, 29 Jun 2000 17:37:12 -0700 (PDT)

> is not handled by the last check.

By pure window updates, do you mean that SEG.SEQ and SEG.ACK stay the same?
I think the check handles that.  If SEG.ACK < SND.UNA, the flow will not 
go to the update path.  So the last check is equivalent to

	SEG.ACK == SND.WL2 && SND.WL1 == SEG.SEQ && SEG.WND > SND.WND

Isn's this pure window update handling since SND.WL2 is just equal to
SEG.ACK for pure window updates?  Or do you mean another thing?  Actually,
I think your revised check is equivalent to Alexey's check, except the order
of checking is different.

> SEG.ACK <= SND.UNA <= SND.MAX.

Typo?  I guess you mean SND.UNA <= SEG.ACK <= SND.MAX.

							K. Poon.
							[email-masked]

---

From: Ramesh Shankar <[email-masked]>
Date: Fri, 30 Jun 2000 07:17:25 -0600

> OR if ((SND.WL2 == SEG.ACK) && (SEG.WND > SND.WND))

What I meant to say was that when receiver is retransmitting, SEG.SEQ
will be < SND.WL1. Hence, the third check must use SND.WL2 to handle
pure ACKs having window updates. This was one of the issues that I
started off the whole discussion with :-)).

Thanks,

S.R.

---

From: [email-masked]
Date: Fri, 30 Jun 2000 22:45:15 +0400 (MSK DST)

Hello!

> OR if ((SND.WL2 == SEG.ACK) && (SEG.WND > SND.WND))

Hey, stop! I tried to code this and immediately found, that
window is updated if:

	if (SEG.SEQ > SND.WL1 ||
	    SEG.ACK > SND.UNA ||
	    SEG.WND > SND.WND)

In other words, it accepts each window expansion not depending on anything.

Are you sure, that you wanted this?

Alexey

---

From: [email-masked]
Date: Fri, 30 Jun 2000 22:28:08 +0400 (MSK DST)

Hello!

> of checking is different.

No, they do not coincide. And Ramesh's version seems to be more clever.

> OR if ((SND.WL2 == SEG.ACK) && (SEG.WND > SND.WND))

includes SND.WL1==SEG.SEQ in my version. I did this because
otherwise it will result in spurious window reopens by old segments.

It is not that problem, which was fighted. Window update is accepted,
when ACK advances, that's that problem and it is solved in the second line.

I understand, why the check is omitted. It is because of the second
difference. My version allows WL1 to go back, when update occurs from
new ACK. This one prohibit this (the first line), so that check
for SND.WL1==SEG.ACK in the third line becomes too restrictive.

The only doubt is that omitting this check looks like artificial stimulation
of window opening. Following this line, we simply can prohibit
(rather than "strongly discourage" 8)) window shrinking at all,
it will be simpler.

Alexey

---

From: Kacheong Poon <[email-masked]>
Date: Fri, 30 Jun 2000 12:04:31 -0700 (PDT)

> started off the whole discussion with :-)).

Hmm, in the original mail, you mentioned that it was a bi-directional
transfer.  So the retransmission should better ack some data.  If it
ack's some data, then the window update should be accepted.  It seems
to me that what you are suggesting here is that TCP should accept window
updates from old segment without new data being acked.  I don't think
it is a good idea.  How can you know for sure that the segment you get
is really a retransmission or just the old original one, if the ack
stays the same.  Do you really mean that?

							K. Poon.
							[email-masked]

---

From: Kacheong Poon <[email-masked]>
Date: Fri, 30 Jun 2000 12:13:35 -0700 (PDT)

> otherwise it will result in spurious window reopens by old segments.

Yup, I missed this line.  But I think we should include SND.WL1==SEG.SEQ.
I just replied to Ramesh asking whether he really means that he wants TCP
to accept window update from "old" segment which does not ack new data.  I
think the problem is that TCP cannot know whether the newly rceived "old"
segment is really a retransmission or the original old segment if ack stays
the same.

> for SND.WL1==SEG.ACK in the third line becomes too restrictive.

I think it is too "liberal" instead of too "restrictive."  It just means that
whenever a segment comes in with a larger window, TCP will accept the update. 
I don't think this is correct.

							K. Poon.
							[email-masked]

---

From: Kacheong Poon <[email-masked]>
Date: Fri, 30 Jun 2000 12:16:26 -0700 (PDT)

> In other words, it accepts each window expansion not depending on anything.

Interesting!  So one really needs to write the code to really know what
one is thinking C:  It is not obvious to me from reading those checks that
they can be reduced to the above!

							K. Poon.
							[email-masked]

---

From: Ramesh Shankar <[email-masked]>
Date: Fri, 30 Jun 2000 14:34:31 -0600

The original algorithm accepts window updates if:

SEG.SEQ > SND.WL1

OR if ((SEG.SEQ == SND.WL1) AND (SEG.ACK > SND.WL2))

OR if ((SEG.ACK == SND.WL2) AND (SEG.WND > SND.WND))

All that we have really changed is check # 2, which doesn't handle the
case of retransmits involving bidirectional data transfer, as reproduced
here again below:

OR if (SEG.ACK >SND.WL2)

Unless someone explains the reason behind not using window information
sent by receiver during retransmits, the above modification would
correctly handle things.

So, window information from a received ACK would be used: [Assuming
trimming has been already done and basic ACK validation has already been
done].

- If the segment has new data
- If the segment ACKs new data
- If this is a window update [announcing a bigger window] that is not
out of order.

Let me know whether there are any issues.

Thanks,

S.R.

---

From: Ramesh Shankar <[email-masked]>
Date: Fri, 30 Jun 2000 14:54:52 -0600

Isn't it possible for a receiver who is retransmitting to us to send
window updates (when the application at the receiving side picked up new
data)? Also, when the receiver is retransmitting, not every segment
needs to ACK new data (perhaps we stopped temporarily, perhaps our data
didn't make it to the receiver), even if it were bidirectional. 

Here is a solid example. Bidirectional data transfer, receiver
advertised zero window, as we filled receiver's window, receiver timed
out and retransmitted (SEG.SEQ < SND.WL1) and then when the application
at the receiver picked up the data, it sent a window update, with
SEG.ACK = SND.WL2. Note that we will be doing window probes (and getting
0 window responses until application at receiver picks up enough data).
Remember that SEG.SEQ for this window update would be less than SND.WL1.
Hence, if we ignore this window update (or use the 3rd check mentioned
by Alexy), until the receiver retransmits all lost data and its SEG.SEQ
crosses SND.WL1, we won't ever be able to send anything.

I don't know what you mean by "old" segments. The basic check of SND.UNA
<= SEG.ACK <= SND.MAX must have been already made when we do the send
window update check. Hence, If the receiver sent an ACK which got
delayed, and during retransmit sent another plain ACK (with a higher ACK
#), and we updated SND.WL2 and the ACK which got delayed is received,
then we would not be accepting that old ack as SEG.ACK < SND.UNA (and
essentially SND.WL2).

Am I missing something very obvious?

Thanks,

S.R.

---

From: Kacheong Poon <[email-masked]>
Date: Fri, 30 Jun 2000 14:59:29 -0700 (PDT)

> didn't make it to the receiver), even if it were bidirectional. 

What I was trying to say is that without ack'ing new data, the receiver
of the segment does not know whether it is the original segment A or a
retransmission of segment A.  That's what I was asking.  I am not saying
that a retransmitted segment cannot have a window update.  If A is somehow
delayed but received and the retransmitted segment is lost, shall TCP use
the window update info in the original segment A?  I think this case can
confuse TCP, thus we may not want the window update.  This is the question
I tried to ask you.  Your answer can be yes, TCP should take it.

> crosses SND.WL1, we won't ever be able to send anything.

The above assertion is not correct.  Since the sender is sending window
probe, if the receiver can open up the window, it also means that it will
take that 1 byte window probe.  This means that it will ack that 1 byte.  So
Alexey's check will take the window update in the retransmitted segment
since it ack's new data.

> then we would not be accepting that old ack as SEG.ACK < SND.UNA (and

The higher ack # assertion is not correct.  I think you've pointed that out
in your first paragraph above.  Not every segments, even retransmitted
segments, needs to ack new data.  It really depends on the traffic pattern.
So TCP cannot really distinguish between the original "old" segment and a
retransmitted segment.  This is the question I tried to ask...

> OR if ((SEG.ACK == SND.WL2) AND (SEG.WND > SND.WND))

I don't think the above is the original BSD checks.  Where did you get it
from?  The BSD check (from actual freebsd code) is

            (SEQ_LT(tp->snd_wl1, ti->ti_seq) ||
            (tp->snd_wl1 == ti->ti_seq && (SEQ_LT(tp->snd_wl2, ti->ti_ack) ||
             (tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd))))

Note that the third check is 

	SND.WL1 == SEG.SEQ && SND.WL2 == SEG.ACK && SEG.WND > SND.WND

This is what Alexey pointed out is the difference I missed in your revised
suggestion and Alexey's suggestion.  I originally thought that they were the
same.  But actually, I missed that fact that you missed the above SND.WL1 ==
SEG.SEQ check.  I guess you also missed this difference C:

							K. Poon.
							[email-masked]

---

From: Ramesh Shankar <[email-masked]>
Date: Fri, 30 Jun 2000 16:22:20 -0600

> Alexey's check will take the window update in the retransmitted segment

I knew you would say this :-)). If you were doing "keep alive" style
window probes, you would run into the problem that I mentioned. I
personally don't like the 1-byte window probe scheme.

Thanks,

S.R.

---

From: Kacheong Poon <[email-masked]>
Date: Fri, 30 Jun 2000 15:36:17 -0700 (PDT)

> personally don't like the 1-byte window probe scheme.

What do you mean by "keep alive" style window probe?  Do you mean probing
zero window every 2 hours?  And what is your objection to 1 byte zero
window probe?

							K. Poon.
							[email-masked]

---

From: Ramesh Shankar <[email-masked]>
Date: Fri, 30 Jun 2000 16:45:30 -0600

> > didn't make it to the receiver), even if it were bidirectional.

Okay, I thought about this one. Let me know whether this looks correct: 

Assume that segment A is the original one sent by the receiver to us
(sender) and it got delayed. Now receiver is retransmitting and it so
happens that old (delayed) segment A comes to us. Here are the cases:

- New A has a newer ACK (i.e. SEG.ACK > SND.WL2). We don't care what
window this segment has. We blindly use it. (The window could have very
well been shrunk by the receiver).

- New A has same ACK (i.e. SEG.ACK == SND.WL2). In this case, we have
two cases:
	* New A's SEG.WND < old A's SEG.WND
If we were to accept old A first and old A had more window, we won't be
accepting New A's ACK information. However, note that this is an illegal
case anyway as with same ACK#, the receiver has shrunk the window. This
won't be accepted by the existing code. (Only "legal" way to shink the
window is to send new data or ACK new data). I hope this one is right!

	* New A's SEG.WND > old A's SEG.WND
This is perfectly okay, this is a window update and even if we were to
accept the old A first and update the window (Note that we would have
done this only if old A's window had SEG.WND > SND.WND), we will use the
new A's window information and update the window again.

- Old A has an older ACK. No problem here. Won't accept the ACK anyway.

I think all the cases have been covered.

>        SND.WL1 == SEG.SEQ && SND.WL2 == SEG.ACK && SEG.WND > SND.WND

I guess you are right. NetBSD 1.41 has the code that I quoted. FreeBSD
3.31 has the one that you quoted. Alexy also pointed this out. I missed
this.

BTW, I am still hoping that someone would enlighten me with some kind of
reasoning on why the standard BSD code did not use window information
contained in retransmissions. i.e. when SEG.SEQ < SND.WL1. I can see
only two cases where this can happen:

* Retransmission
* Out of order segment.

Out of order segments are not the issue. I wonder whether there is some
kind of really messy case in retransmission which prompted the standard
BSD scheme. Or may be I am paranoid :-)).

Thanks,

S.R.

---

From: Kacheong Poon <[email-masked]>
Date: Fri, 30 Jun 2000 15:45:31 -0700 (PDT)

> personally don't like the 1-byte window probe scheme.

On a second thought, I guess by keep alive style, you mean sending a fake
byte or use old sequence number to send the zero window probe.  Do I guess
correctly?

							K. Poon.
							[email-masked]

---

From: Kacheong Poon <[email-masked]>
Date: Fri, 30 Jun 2000 16:19:47 -0700 (PDT)

> well been shrunk by the receiver).

Actually, I think TCP will not take new A (A*)'s window update, even with the
proposed change.  The reason is that A* is a duplicate data segment.  It is
different from your original case when the retransmitted segment fills in a
hole.  So if A is received first and then A* arrives, A* will be dropped.

> window is to send new data or ACK new data). I hope this one is right!

This looks correct.

> new A's window information and update the window again.

Again, TCP will not use A*'s window update because it is a duplicate data
segment.

> I think all the cases have been covered.

So I think the above cases can tell us that it can be confusing on which
update TCP should take or should not take.  Well, to avoid this confusion,

> BSD scheme. Or may be I am paranoid :-)).

That's why I suggested Alexey to send the proposed change to end2end.  They
have old timers there and they can tell us if the proposed change misses
some cases.

							K. Poon.
							[email-masked]