Subject: RE: [Tsvwg] Re: U-SCTP comments

From: [email-masked]
Date: Fri, 16 Mar 2001 15:46:45 +0200

Hi all!

	See below...

> [email-masked] writes:
> > > ["La Monte Henry Piggy Yarroll" <[email-masked]> wrote:]
> > > Is there are fast way to detect packets with P-DATA 
> chunks in them?
> > > I'd really rather not pollute my checksum fast-path with them.
> > 
> > 	What about using with them the same restriction as with 
> COOKIE ECHO
> > and COOKIE ACK? I will propose that in case a datagram 
> carries any P-DATA,
> > the first chunk in the datagram should be a P-DATA chunk... 
> So we should
> > just look for the value 0xc3 in the first two bytes after 
> the header (bytes
> > 13th and 14th)... It would be a quickly check, and in case 
> that number is
> > there, we should make the parsing before verifying if the 
> checksum is
> > correct, otherwise not...
> > 
> > 	This can bring up some problems, since the DATA chunks 
> (and I guess
> > that the P-DATA chunks as well, that's what logically I 
> would do, but it is
> > not explicitly stated... Maybe the end of section 5 would 
> be the best place
> > to state it...) must be inside the datagram in ascending 
> order of TSNs (RFC
> > 2960, section 6.10, third paragraph), so, in case there are 
> DATA and P-DATA
> > chunks mixed in the same datagram, the first P-DATA should 
> have a TSN
> > previous to any other TSN in that datagram... This would 
> affect the way we
> > bundle the chunks inside a datagram... nothing that 
> couldn't be done...
> > Also, if an application makes use of these P-DATA chunks, 
> maybe it won't
> > even use the DATA chunks, so this restriction won't even 
> been noticed...
> > 
> > 	Any comments about this? Thanks!
> 
> I like this solution.  I was going to propose a P-DATA-PRESENT control
> chunk that would have to be the first chunk in the packet, but just
> saying that you can't bundle P-DATA with control chunks (AND the first
> P-DATA must be the first chunk) accomplishes much the same thing
> without a new control chunk.  Hmm... that DOES mean most of your SACKs
> in an unreliable association would have to travel stand-alone.

	Thinking about how to avoid this, I have a proposal that avoids
sending SACKs on their own, and at the same time makes much easier the
calculation of the checksum in case of unreliable DATA chunks included in
the datagram... This is what I have thought about (I won't do it again, it
hurts! ;-)):

	- Firstly, we should have a new chunk, called Partial Checksum
Coverage (P-CHECKSUM). It would be like this:

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |1 1 0 0 0 0 1 1|  Chunk Flags  |    Chunk Length = 4 + N*4     |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | Byte Block* Uncovered Start #1|  Byte Block* Uncovered End #1 |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   \                                                               \
   /                              ...                              /
   \                                                               \
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | Byte Block Uncovered Start #N |  Byte Block Uncovered End #N  |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

	* One Byte Block is simply a piece of the datagram.

	- The type would be the same as the one assigned to the P-DATA chunk
since that new chunk wouldn't be necessary any more (as you will see later).

	- This would be a control chunk that would have to be the first
chunk in the datagram (right after the common SCTP header), and should be
included in case any of the DATA chunks inside the datagram is only
partially covered by the Adler-32 Checksum.

	- Inside the datagram, the bytes starting from the one in position
"Byte Block Uncovered Start #X" (and including it) to "Byte Block Uncovered
End #X" (also this one included) are NOT covered by the Adler-32 Checksum.
All the rest (from byte number 0 to ("Byte Block Uncovered Start #1" - 1);
from ("Byte Block Uncovered End #X" + 1) to ("Byte Block Uncovered Start
#X+1" - 1); and from ("Byte Block Uncovered End #N" + 1) to the end of the
datagram (including padding)), ARE covered by the Adler-32 Checksum.

	- These byte blocks that are not covered by the Adler-32 Checksum,
should primarily be located inside the "User Data" part of DATA chunks, but
I don't know if this should be a must... Some other future chunks, or even
the ones that already exist, can be also partially covered by the checksum.
However, I think that this is not advisable (why doing it?). In any case, if
the goal is not parsing the datagram, that shouldn't be verified by the
receiver of the datagram.

	- The header of all the chunks in the datagram should always be
covered by the Adler-32 Checksum. This also makes that one uncovered byte
block should be located inside one chunk's bounds (the same block can't
affect more that one chunk), but there can be several byte blocks located
inside the same chunk.

	- The common header, and the whole P-CHECKSUM should always be
covered by the Adler-32. So, "Byte Block Uncovered Start #1" should be at
least: 12 (common header) + 4 + N*4 (P-CHECKSUM chunk) + 4 (next chunk
header) = 20 + N*4. If the restriction that only the "User Data" part of
DATA chunks can be uncovered, as I think is the logical way, that minimum
value would be: 12 + 4 + N*4 + 16 (fixed part of a DATA chunk) = 32 + N*4.

	- "Byte Block Uncovered Start #X" should be strictly smaller than
"Byte Block Uncovered End #X", for every byte block in the P-CHECKSUM. Also,
for every X and X+1 consecutive byte blocks, "Byte Block Uncovered End #X"
should be strictly smaller than "Byte Block Uncovered Start #X+1". In other
words, the byte blocks should be at least 1 byte length, and the "distance"
between byte blocks should be at least 1 byte. This is done not to make the
Adler-32 calculation unnecessarily longer.

	- "Byte Block Uncovered End #N" shouldn't be bigger than the length
of the SCTP datagram. The same can be said about any other "Byte Block
Uncovered Start" or "Byte Block Uncovered End"...

	- I think that we could allow not including any byte block at all
inside the P-CHECKSUM. This is because, for the shake of the simplicity, one
implementation could choose including the P-CHECKSUM always after the common
header. This could make things easier for the MTU restriction calculation, I
mean, when you bundle a DATA chunk with partial checksum coverage, you know
you have to add 4 more bytes (as with the P-DATA option) to set the
beginning and ending of the uncovered block (supposing you only have one
uncovered block in that DATA chunk), but the 4 bytes of the header of the
P-CHECKSUM must be added only the first time you add a partial covered DATA
chunk...

	- Maybe the DATA chunks with part of its "User Data" not covered by
the checksum could have a new flag set... But I don't think this would
really give you extra information at all or how this can be helpful...

	So, let's illustrate the "conversion" between the way of achieving
the not inclusion of some data inside the Adler-32 as stated in the draft,
and the new way proposed. Let's consider this datagram:

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     Source Port Number        |     Destination Port Number   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                      Verification Tag                         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           Checksum                            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Type = SACK  |  Chunk  Flags |       Chunk Length = 20       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                      Cumulative TSN Ack                       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          Advertised Receiver Window Credit (a_rwnd)           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | Number of Gap Ack Blocks = 1  |  Number of Duplicate TSNs = 0 |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |    Gap Ack Block #1 Start     |     Gap Ack Block #1 End      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | Type = P-DATA | Reserved|U|B|E|       Length = 64             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                              TSN                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |      Stream Identifier S      |   Stream Sequence Number n    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                 Payload Protocol Identifier                   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                  Checksum Coverage = 4                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   \                                                               \
   /                 User Data (seq n of Stream S)                 /
   \                                                               \
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | Type = P-DATA | Reserved|U|B|E|       Length = 104            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                              TSN                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |      Stream Identifier S      |   Stream Sequence Number n    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                 Payload Protocol Identifier                   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                  Checksum Coverage = 48                       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   \                                                               \
   /                 User Data (seq n of Stream S)                 /
   \                                                               \
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Type = DATA  | Reserved|U|B|E|       Length = 80             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                              TSN                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |      Stream Identifier S      |   Stream Sequence Number n    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                 Payload Protocol Identifier                   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   \                                                               \
   /                 User Data (seq n of Stream S)                 /
   \                                                               \
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

	Using the P-CHECKSUM chunk, that SCTP datagram would become this
other one:

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     Source Port Number        |     Destination Port Number   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                      Verification Tag                         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           Checksum                            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |Type=P-CHECKSUM|  Chunk Flags  |       Chunk Length = 12       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |Byte Block Uncovered Start = 64|Byte Block Uncovered End = 103 |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |Byte Block Uncovered Start =168|Byte Block Uncovered End = 203 |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Type = SACK  |  Chunk  Flags |       Chunk Length = 28       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                      Cumulative TSN Ack                       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          Advertised Receiver Window Credit (a_rwnd)           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | Number of Gap Ack Blocks = 1  |  Number of Duplicate TSNs = 0 |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |    Gap Ack Block #1 Start     |     Gap Ack Block #1 End      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Type = DATA  | Reserved|U|B|E|       Length = 60             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                              TSN                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |      Stream Identifier S      |   Stream Sequence Number n    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                 Payload Protocol Identifier                   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   \                                                               \
   /                 User Data (seq n of Stream S)                 /
   \                                                               \
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Type = DATA  | Reserved|U|B|E|       Length = 100            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                              TSN                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |      Stream Identifier S      |   Stream Sequence Number n    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                 Payload Protocol Identifier                   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   \                                                               \
   /                 User Data (seq n of Stream S)                 /
   \                                                               \
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Type = DATA  | Reserved|U|B|E|       Length = 80             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                              TSN                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |      Stream Identifier S      |   Stream Sequence Number n    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                 Payload Protocol Identifier                   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   \                                                               \
   /                 User Data (seq n of Stream S)                 /
   \                                                               \
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

	Thinking about advantages and disadvantages of the change, I would
point these. Disadvantages... I can now think about these:

	- You use 4 more bytes than with the P-DATA usage option. I hope
that this is not that important...
	- When processing internally the DATA chunks, you don't know which
bytes have been covered by the Adler-32 Checksum and which not (at least not
that easily). Since you don't pass that information to the upper user, I
don't think that this is really important...
	- You need to feel this P-CHECKSUM chunk. But, since you really have
to know those byte positions when calculating the Adler-32 Checksum in your
outgoing datagrams, I think that this could be very easily to do...
	- You need to modify the draft in case you like it... :-)

	About the advantages... Maybe these ones:

	- It doesn't include the addition of any new type of chunk. You add
the P-CHECKSUM type, and delete the P-DATA type...
	- It is not necessary that the uncovered data is at the beginning of
the chunk. Also, there can be several uncovered byte blocks inside the same
DATA chunk. I don't know if this is important, but it gives you more
freedom...
	- The calculation of the Adler-32 Checksum takes almost the same
time as before. If the P-CHECKSUM is not the first chunk in the datagram, it
means that it is not there at all, and the calculation is almost exactly the
same as before, it only takes one more comparison, to see if byte in
position 12 is 0xc3.  If we allow including P-CHECKSUM chunks without byte
blocks, we should also check that bytes 14 and 15 make a two bytes unsigned
integer bigger than 8. If the P-CHECKSUM is there and includes at least one
byte block, the knowledge about which specific bytes are covered and which
are not is passed from the sender to the receiver in a neat way, without
extra calculation...
	- As the P-CHECKSUM is a control chunk, there isn't any problem
about putting it before any SACK chunk. So the problem about sending SACK
chunks on standalone datagrams is also solved.
	- The sending of user data is done in a regular way. From the
receivers point of view, there is no difference between data sent covered by
the checksum or not, since they are equally sent in DATA chunks. You don't
need to explain things in the draft regarding the differences between DATA
and P-DATA chunks, since there isn't such a difference any more.

	So, what the authors think about this? I hope this can be of any
help...

	Thanks!

	BR Iván Arias Rodríguez

_______________________________________________
tsvwg mailing list
[email-masked]
http://www1.ietf.org/mailman/listinfo/tsvwg

---

From: Qiaobing Xie <[email-masked]>
Date: Fri, 16 Mar 2001 11:58:11 -0600

Ivan,

Interesting proposal! But the issue raised by LaMonte still concerns me
and your this proposal doesn't change it. You still need to parse
through the data structure __before__ you can validate the checksum/data
integrity. Your proposal just makes the calculation easier. 

I think this issue is fundamental and needs to be addressed somehow. 

I am also contemplating to put the partial checksum DATA into a separate
draft from U-SCTP draft, just to ease the discussions.

-Qiaobing

---

From: "La Monte Henry Piggy Yarroll" <[email-masked]>
Date: Sat, 17 Mar 2001 06:26:33 -0600

> draft from U-SCTP draft, just to ease the discussions.

Please don't do this.  Partial checksum really does depend on
the unreliable extension.  Separating pcheck may make it easier to
finish the work on usctp, but it greatly decreases the odds of getting
pcheck implemented.  How many vendors do you know who habitually
implement extensions of extensions?
-- 
La Monte H.P. Yarroll      Home:                [email-masked]
   If you remember nothing else:  [email-masked]         NIC Handle: LY
   GPL - "Just give source a chance."

---

From: "La Monte Henry Piggy Yarroll" <[email-masked]>
Date: Sat, 17 Mar 2001 07:43:33 -0600

[email-masked] writes:
> 	Thinking about how to avoid [standalone SACKs], I have a
> proposal that avoids sending SACKs on their own, and at the same
> time makes much easier the calculation of the checksum in case of
> unreliable DATA chunks included in the datagram... This is what I
> have thought about (I won't do it again, it hurts! ;-)):

Excellent!  I have a completely different proposal but I'm cribbing
heavily from Ivan's suggestion :-).

I steal Ivan's P-CHECKSUM (though simplified), and I propose a new
split representation for P-DATA.

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |1 1 0 0 0 1 1 1|  Chunk Flags  |    Chunk Length = 8           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |        Covered Length         |          Padding              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        - Note that I just changed one random bit in the chunk
	type--be sure to check that number for safe inclusion...

        - If present, this MUST be the first chunk in the packet.

	- Covered Length is the (contiguous) amount of the packet
	covered by the Adler-32 Checksum relative to the start of the
	SCTP header.

	- All unprotected data starts at byte Covered_Length in the
	packet. 

I have to add the following code to my checksum fastpath:

        if (P_CHECKSUM == packet[FIXED_OFFSET]) {
                csum_len = ntohs(((p_checksum_header *)(packet + FIXED_OFFSET))
                                  ->covered_length);
        }

        /* Be paranoid since covered_length is not protected by checksum. */
        if (csum_len < MIN_HEADER || csum_len > iphdr.pktlen) {
                return(BOGUS_PACKET);
        }
        ...

- Secondly, P-DATA looks like this:

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |1 1 0 0 0 0 1 1| Reserved|U|B|E|    Length                     |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                              TSN                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |      Stream Identifier S      |   Stream Sequence Number n    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                 Payload Protocol Identifier                   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Unprotected Data Length                    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   \                                                               \
   /                Protected User Data (seq n of Stream S)        /
   \                                                               \
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                .
                                .
                                .
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   \                                                               \
   /              Unprotected User Data (seq n of Stream S)        /
   \                                                               \
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

        - Length only coveres the header through the end of the user
        data.

        - Unprotected Data Length gives the length of the unprotected
        user data at the end of the packet.

On systems with mbufs this split representation is easy and fast to
implement on both the sending and receiving sides--the end user need
never see it.

This solves BOTH the problems I identified before:

1) We minimize our exposure to unchecked data (to 3 bytes).

2) P-DATA detection is as cheap as it can possibly get.

And it also answers one of Jon Berger's issues:

3) All remaining special code is restricted to the P-DATA processing
functions.

It solves the same problems Ivan's solution does (in addition to the above)

4) We can now bundle SACKs again.

5) A further bonus is a noticable decrease in CPU cycles wasted on
critically damaged packets.

I look forward to seeing folks in Minneapolis!  I'll be leaving in
about an hour...