Subject: iSCSI Data Integrity - Digests

From: Michael Krause <[email-masked]>
Date: Fri, 26 Jan 2001 18:31:31 -0800

The following describes three alternatives for iSCSI data integrity.

Mike Krause
HP

Requirements:
    * Use existing / proven CRC algorithms and techniques to provide fast 
market enablement while avoiding a "reinvention of the wheel" exercise.
    * Provide strong end-to-end data integrity for both iSCSI PDU header 
and data payload
    * CRC is required for all implementations,  i.e. strong end-to-end data 
integrity is not an option as customers will not adopt solutions without 
such guarantees.
Alternative 1 (Highest preference):
    * An iSCSI PDU shall be restricted to a single TCP segment.  Multiple 
iSCSI PDU may be present within the same TCP segment but none shall span 
multiple segments.
    * Each iSCSI PDU is protected by a trailing 32-bit CRC (Ethernet 
polynomial), i.e. a single CRC covers the entire iSCSI header and data.
Assumption:
    * iSCSI PDU header is not modified during transmission.  While there 
has been some discussion of a desire to provide such capabilities in the 
future, there are no current requirements to support requiring the 
specification to take this into account at this time. Should this become a 
requirement in the future, an intermediate endnode supporting iSCSI header 
modification would need to guarantee strong data integrity within its 
implementation using any of the well-known / deployed techniques.
Benefits:
    * Strong end-to-end data integrity using a well-known, proven technology.
    * Low-cost, high-speed hardware implementations with readily available 
hardware cores can be created with minimal design complexity.
    * Only one CRC which can be implemented in software mitigating the 
performance impacts this iSCSI data integrity would impose.
    * Ability to accelerate software iSCSI implementations using a slightly 
modified NIC to perform the CRC calculation / verification for both inbound 
and outbound data streams.  This modified NIC would only require minor 
understanding of the iSCSI header, i.e. to identify it and locate the CRC 
within the data stream. The CRC can be verified while coming in off the 
"wire" or inserted while being placed on the "wire".   This technique is 
well understood since it is very similar to what is implemented by TCP 
checksum off-load implementations in use today.
        * Note: A NIC implementing this functionality could combine the 
verification of the TCP checksum into a "one-stop" verification operation 
and silently drop invalid packets or tag them as "bad" for ULP processing.
    * Solves the framing problem while eliminating the need for future 
support of "chunking" / RDMA technology.  Each PDU header contains 
sufficient information required for direct data placement providing the 
same benefits attributed to chunking / RDMA.  This will also allow 
simplified "bridge" solutions to be constructed, e.g. iSCSI-to-InfiniBand, 
iSCSI-to-SRP, etc.
    * Eliminates the need to maintain intermediate CRC results (both 
inbound and outbound) reducing implementation cost / complexity.
    * Eliminates bandwidth waste by reducing the number of bytes required 
to guarantee end-to-end data integrity while supporting multiple small PDU 
per segment (compaction)
    * Provides improved QoS arbitration control / management - if a PDU 
were allowed to span multiple segments, then an implementation would need 
to transmit segments back-to-back (or very close) to deliver strong 
end-to-end performance / transaction throughput.  This may be 
implementation-specific but is still a tangible benefit for customers.
    * If an intermediate endnode performs re-segmentation, a PDU may be 
span multiple segments.  This would be detected by a PDU CRC error 
providing a simple detection mechanism allowing implementations to recover 
either at the connection or session level.
Constraints:
    * iSCSI implementations must be able to determine each connection's MSS 
and create iSCSI PDU that fit within the MSS.  Such functionality is 
available in a variety of TCP implementations today and for hardware 
implementations.
        * For the send-side retransmission problem (i.e. how to delineate 
packets within a byte stream), a hardware implementation is 
straight-forward to support since it provide the PDU-segment correlation.
        * For a software implementation, the mbuf / mblk encompassing the 
iSCSI PDU would be marked to indicate whether the associated buffer should 
be sent within a separate segment or not.  This is not common to any TCP 
implementations to date but is not difficult to implement.  It should also 
be noted that this is an implementation not a TCP protocol issue.
        * If a layer 4 intermediate endnode glues together two TCP streams 
and is not iSCSI aware, the send-side retransmission is a 
problem.  However, it is unclear whether this usage model must be 
transparently supported by iSCSI, i.e. such an intermediate endnode should 
be required to be iSCSI aware.  This is not unreasonable as most layer 4 
intermediate endnodes are providing some value-add service as a function of 
layer 4; why wouldn't such an endnode provide iSCSI value-add and thus be 
layer 5 aware.

Alternative 2 (middle preference)
    * An iSCSI PDU shall be restricted to a single TCP segment.  Multiple 
iSCSI PDU may be present within the same TCP segment but none shall span 
multiple segments.
    * Each iSCSI PDU is protected by two CRCs - one invariant and one 
variant.  The invariant CRC (ICRC) is a 32-bit CRC covering the PDU data 
and invariant header fields (e.g. address).  The variant CRC (VCRC) is 
either a 16 or 32-bit CRC that covers the entire PDU header, data, and 
invariant CRC.  PDU layout would be: header, data, ICRC, VCRC.
        * Note: This scheme is conceptually the same as what is used in 
InfiniBand providing customers and the industry with a single paradigm and 
improved technology integration for both compute and storage endnodes.

Benefits relative to Alternative 1:
    * Supports an intermediated endnode updating iSCSI header fields while 
supporting strong end-to-end data integrity of all invariant header fields 
and data.  It is critical that all invariant header fields such as target 
address be protected at all times to avoid silent data corruption / illegal 
memory access since these fields are used to DMA the data into / from 
target memory.
        * Note: This problem does not exist in IP-based applications today 
since such implementations do not expose addresses across the wire but use 
look-up techniques as a function of the header.  iSCSI implementations may 
choose to use a similar technique but at the cost of increased resources / 
complexity.
    * Limits the complexity / overhead required to support a separate 
header CRC - e.g. intermediate byte-stream CRC injection / 
verification.  This simplifies the hardware implementation for full 
off-load solution as well as provides the ability to create simplified CRC 
acceleration as described in alternative 1 for software-based iSCSI 
implementations.
    * Use of two trailer CRCs does not impact overall end-to-end 
performance or endnode hardware resources.  Implementations are gated more 
by the memory subsystems / cache coherency overheads than by external wire 
speed transmission, i.e .the packet will, in general, arrive before one 
could complete the first few cache line fetch operations.   As such, given 
the single-segment operation, the data can be verified as it comes in off 
the wire and the memory operations initiated with minimal latency (most 
operations will be pipeline operations within a few cycles).
    * An intermediate endnode can provide data integrity checks while data 
is in-flight and stomp the CRC should it detect an error.  This allows 
packet flow-through to be supported while providing fault isolation and a 
single for subsequent endnodes to drop invalid packets if they desire.
Constraints:
    * Invariant header fields must be identified and included within the 
ICRC calculation adding minor complexity to the overall implementation.

Alternative 3 (least preferred):
    * Allow a PDU to span multiple TCP segments.
    * Implement two CRC: a header CRC and a data CRC.
    * Do not allow intermediate endnodes to modify the iSCSI header.
Constraints / Disadvantages:
    * Increased implementation complexity and overhead.  The header CRC 
must occur following the header requiring injection / removal within the 
endnodes.  This complexity is compounded for variable header protocols such 
as iSCSI and is why such a solution has been rejected in other high-speed 
technologies.
    * Requires intermediate CRC state to be maintained for both inbound and 
outbound requests.
    * Increased QoS scheduling complexity for strong end-to-end application 
throughput.
    * Does not solve the framing problem perhaps necessitates the need for 
a chunking / RDMA solution.  This increases solution complexity and creates 
interoperability / support issues for customers, i.e. options are bad for 
developers; bad for customers.
    * Severely limits creating high performance iSCSI software-based 
implementations perhaps making them impractical as a general purpose 
implementation.  This will limit the potential market for iSCSI solutions.
    * Note: If an intermediate endnode is allowed to modify the PDU header, 
then there exists a possibility of silent data corruption since the 
invariant portions no longer have end-to-end data integrity.  This will be 
a major issue for customers in terms of their ability to adopt iSCSI across 
a variety of solution spaces, i.e. if there is the potential for silent 
data corruption, then customers will not deploy iSCSI and will turn to 
alternatives that provide stronger end-to-end data integrity.

---

From: "Douglas Otis" <[email-masked]>
Date: Mon, 29 Jan 2001 10:13:34 -0800

Michael,

Your highest and second highest preference represents a change to the TCP
protocol in that it mandates segment alignment of the PDU with the Ethernet
frame.  The impact of changing TCP into a datagram protocol may not be well
received.  If such a requirement exists, use SCTP.  If you wish to see two
frame checks, one for the iSCSI header and one again for the SCSI payload, I
would suggest that there are variables used as handshakes within the iSCSI
header that could use even a simpler scheme than a 16 bit CRC, a 32 bit XOR.
If done in software, both the TCP checksum and this header checksum will
require updating.  A single CRC field will represent a time constraint in
that a pipeline delay of iSCSI feedback will exist. Allowing these
calculations to take place prior to being placed for delivery within an
interrupt routine is the advantage of allowing a simple feedback update.

Doug

Mike Krause
HP

Requirements:
Use existing / proven CRC algorithms and techniques to provide fast market
enablement while avoiding a "reinvention of the wheel" exercise.
Provide strong end-to-end data integrity for both iSCSI PDU header and data
payload
CRC is required for all implementations,  i.e. strong end-to-end data
integrity is not an option as customers will not adopt solutions without
such guarantees.
Alternative 1 (Highest preference):
An iSCSI PDU shall be restricted to a single TCP segment.  Multiple iSCSI
PDU may be present within the same TCP segment but none shall span multiple
segments.
Each iSCSI PDU is protected by a trailing 32-bit CRC (Ethernet polynomial),
i.e. a single CRC covers the entire iSCSI header and data.

Assumption:
iSCSI PDU header is not modified during transmission.  While there has been
some discussion of a desire to provide such capabilities in the future,
there are no current requirements to support requiring the specification to
take this into account at this time. Should this become a requirement in the
future, an intermediate endnode supporting iSCSI header modification would
need to guarantee strong data integrity within its implementation using any
of the well-known / deployed techniques.

Benefits:
Strong end-to-end data integrity using a well-known, proven technology.
Low-cost, high-speed hardware implementations with readily available
hardware cores can be created with minimal design complexity.
Only one CRC which can be implemented in software mitigating the performance
impacts this iSCSI data integrity would impose.

Ability to accelerate software iSCSI implementations using a slightly

Note: A NIC implementing this functionality could combine the verification
of the TCP checksum into a "one-stop" verification operation and silently
drop invalid packets or tag them as "bad" for ULP processing.

Solves the framing problem while eliminating the need for future support of
"chunking" / RDMA technology.  Each PDU header contains sufficient
information required for direct data placement providing the same benefits
attributed to chunking / RDMA.  This will also allow simplified "bridge"
solutions to be constructed, e.g. iSCSI-to-InfiniBand, iSCSI-to-SRP, etc.
Eliminates the need to maintain intermediate CRC results (both inbound and
outbound) reducing implementation cost / complexity.

Eliminates bandwidth waste by reducing the number of bytes required to
guarantee end-to-end data integrity while supporting multiple small PDU per
segment (compaction)
Provides improved QoS arbitration control / management - if a PDU were
allowed to span multiple segments, then an implementation would need to
transmit segments back-to-back (or very close) to deliver strong end-to-end
performance / transaction throughput.  This may be implementation-specific
but is still a tangible benefit for customers.

If an intermediate endnode performs re-segmentation, a PDU may be span
multiple segments.  This would be detected by a PDU CRC error providing a
simple detection mechanism allowing implementations to recover either at the
connection or session level.

Constraints:
iSCSI implementations must be able to determine each connection's MSS and
create iSCSI PDU that fit within the MSS.  Such functionality is available
in a variety of TCP implementations today and for hardware implementations.

For the send-side retransmission problem (i.e. how to delineate packets
within a byte stream), a hardware implementation is straight-forward to
support since it provide the PDU-segment correlation.
For a software implementation, the mbuf / mblk encompassing the iSCSI PDU
would be marked to indicate whether the associated buffer should be sent
within a separate segment or not.  This is not common to any TCP
implementations to date but is not difficult to implement.  It should also
be noted that this is an implementation not a TCP protocol issue.
If a layer 4 intermediate endnode glues together two TCP streams and is not
iSCSI aware, the send-side retransmission is a problem.  However, it is
unclear whether this usage model must be transparently supported by iSCSI,
i.e. such an intermediate endnode should be required to be iSCSI aware.
This is not unreasonable as most layer 4 intermediate endnodes are providing
some value-add service as a function of layer 4; why wouldn't such an
endnode provide iSCSI value-add and thus be layer 5 aware.

Alternative 2 (middle preference)
An iSCSI PDU shall be restricted to a single TCP segment.  Multiple iSCSI
PDU may be present within the same TCP segment but none shall span multiple
segments.

Each iSCSI PDU is protected by two CRCs - one invariant and one variant.
The invariant CRC (ICRC) is a 32-bit CRC covering the PDU data and invariant
header fields (e.g. address).  The variant CRC (VCRC) is either a 16 or
32-bit CRC that covers the entire PDU header, data, and invariant CRC.  PDU
layout would be: header, data, ICRC, VCRC.

Note: This scheme is conceptually the same as what is used in InfiniBand
providing customers and the industry with a single paradigm and improved
technology integration for both compute and storage endnodes.

Benefits relative to Alternative 1:
Supports an intermediated endnode updating iSCSI header fields while
supporting strong end-to-end data integrity of all invariant header fields
and data.  It is critical that all invariant header fields such as target
address be protected at all times to avoid silent data corruption / illegal
memory access since these fields are used to DMA the data into / from target
memory.

Note: This problem does not exist in IP-based applications today since such
implementations do not expose addresses across the wire but use look-up
techniques as a function of the header.  iSCSI implementations may choose to
use a similar technique but at the cost of increased resources / complexity.

Limits the complexity / overhead required to support a separate header CRC -
e.g. intermediate byte-stream CRC injection / verification.  This simplifies
the hardware implementation for full off-load solution as well as provides
the ability to create simplified CRC acceleration as described in
alternative 1 for software-based iSCSI implementations.

Use of two trailer CRCs does not impact overall end-to-end performance or
endnode hardware resources.  Implementations are gated more by the memory
subsystems / cache coherency overheads than by external wire speed
transmission, i.e .the packet will, in general, arrive before one could
complete the first few cache line fetch operations.   As such, given the
single-segment operation, the data can be verified as it comes in off the
wire and the memory operations initiated with minimal latency (most
operations will be pipeline operations within a few cycles).

An intermediate endnode can provide data integrity checks while data is
in-flight and stomp the CRC should it detect an error.  This allows packet
flow-through to be supported while providing fault isolation and a single
for subsequent endnodes to drop invalid packets if they desire.

Constraints:
Invariant header fields must be identified and included within the ICRC
calculation adding minor complexity to the overall implementation.

Alternative 3 (least preferred):
Allow a PDU to span multiple TCP segments.
Implement two CRC: a header CRC and a data CRC.
Do not allow intermediate endnodes to modify the iSCSI header.

Constraints / Disadvantages:
Increased implementation complexity and overhead.  The header CRC must occur
following the header requiring injection / removal within the endnodes.
This complexity is compounded for variable header protocols such as iSCSI
and is why such a solution has been rejected in other high-speed
technologies.

Requires intermediate CRC state to be maintained for both inbound and
outbound requests.
Increased QoS scheduling complexity for strong end-to-end application
throughput.
Does not solve the framing problem perhaps necessitates the need for a
chunking / RDMA solution.  This increases solution complexity and creates
interoperability / support issues for customers, i.e. options are bad for
developers; bad for customers.

Severely limits creating high performance iSCSI software-based
implementations perhaps making them impractical as a general purpose
implementation.  This will limit the potential market for iSCSI solutions.

Note: If an intermediate endnode is allowed to modify the PDU header, then
there exists a possibility of silent data corruption since the invariant
portions no longer have end-to-end data integrity.  This will be a major
issue for customers in terms of their ability to adopt iSCSI across a
variety of solution spaces, i.e. if there is the potential for silent data
corruption, then customers will not deploy iSCSI and will turn to
alternatives that provide stronger end-to-end data integrity.

---

From: Michael Krause <[email-masked]>
Date: Mon, 29 Jan 2001 12:45:07 -0800

At 10:13 AM 1/29/2001 -0800, Douglas Otis wrote:
>frame.

This is not a change to the TCP protocol - strictly implementation specific.

>The impact of changing TCP into a datagram protocol may not be well
>received.

It is still byte streaming but iSCSI PDUs don't span multiple 
segments.  This is an application-specific issue, i.e. it is the unit of 
how one sends data and has nothing to do with the TCP protocol 
itself.  This really isn't any different than what applications do today - 
a peek() to determine how to cast the byte stream into a message - which is 
very common for most sockets applications.

>  If you wish to see two frame checks, one for the iSCSI header and one 
> again for the SCSI payload, I would suggest that there are variables used 
> as handshakes within the iSCSI

Disagree with your recommendations and analysis.  There is no change 
required for the TCP checksum this proposal is strictly above the TCP 
layer, i.e. iSCSI.

Mike

---

From: "Douglas Otis" <[email-masked]>
Date: Mon, 29 Jan 2001 15:07:58 -0800

Michael,

> very common for most sockets applications.

Are you saying the network adapter stack becomes starved to the point of
only seeing one potential Ethernet frame at a time?

> required for the TCP checksum this proposal is strictly above the TCP

Perhaps I should have not also included feedback on your suggestion of
splitting of headers in conjunction with objecting to your datagram
proposal.  I was not saying to change TCP, only that an error checking
supplement for a small handshake field could be kept separate from the
static SCSI payload.  A simple check scheme may be effective if applied over
a few words of feedback.  Perhaps a SCSI interface driver encapsulates SCSI
payload in a pipeline fashion.  As the stack is ready, feedback is added to
the constructed frame near when the TCP is checksum is updated.  A natural
place to divide these headers checks would be with respect to dynamic
feedback.  The rest of the payload may have already had CRC and partial
checksums calculated in place waiting for placement on the network.  If
these segments are prepared within an interrupt routine, the overhead to
include feedback is important.  A handful of instructions to calculate CRC
could be replace by a single instruction as in the MTF standard.  Wanting to
indicate a possible consideration of where to delineate a header split to
include the static portion of the header together with data as one check and
the dynamic portions of the header as another.

Doug

---

From: Sean Quinlan <[email-masked]>
Date: Mon, 29 Jan 2001 22:51:55 -0500

I would suggest that if iSCSI requires that a PDU does not span a
TCP segment then iSCSI is indeed changing TCP.  A valid implementations
of TCP has considerable freedom in deciding how to pack data into segments
and this is not under the control of the application layer.  Such a
requirement in iSCSI would mean it is not feasible to implement iSCSI on
many existing TCP stacks.  If you change the requirements of for a valid TCP
implementation I believe you have effectively changed the protocol.
I agree with Douglas Otis that such a change may not be well received.

What would you suggest for situations such a iSCSI over a protocol such as TLS(SSL).
TLS uses an internal framing mechanism that is not under the applications
control and is also not related to the underlying TCP segments.

It would seem to me a little unwise to mandate end-to-end data integrity within iSCSI
especially using a CRC algorithm.  Rather a lot of data goes over protocols
such as HTTP, FTP, NFS and CIFS without any additional end-to-end data integrity
over what is provided by TCP and link level integrity - I think it is a little
strong to say 
	strong end-to-end data integrity is not an option as customers will
	not adopt solutions without such guarantees.
If TLS or IPsec are used in conjunction with iSCSI, then strong
end-to-end integrity is already provided and duplicating this in iSCSI seem
rather a waste.  It may be the case that some people are not satisfied with
the integrity provided by TCP and yet do not want to use TLS or IPsec to ensure
integrity/authenticity, but I suspect that such people are not the majority and
thus mandating a data integrity mechanism within iSCSI seems like a mistake -
perhaps as an option or perhaps leave it to other layers in the protocol stack.

If data integrity is provided by iSCSI, CRC algorithms are not particularly
ideal from a software implementation point of view - surely there
are better alternatives than CRC that are easy to implement in hardware and
can take advantage of 32bit wide data ops.

---

From: Matt Wakeley <[email-masked]>
Date: Mon, 29 Jan 2001 22:01:41 -0800

>      iSCSI PDU may be present within the same TCP segment but none shall
>      span multiple segments.

>      iSCSI PDU may be present within the same TCP segment but none shall
>      span multiple segments.

Mike,

While your suggestions do not require a change to the way TCP looks on the
wire, it requires a change in the TCP API. You are going down a rat hole that
we've already attempted, and were repudiated by the TCP gods.

-Matt

---

From: Glen Turner <[email-masked]>
Date: Wed, 31 Jan 2001 11:00:03 +1030

Sean Quinlan wrote:

> rather a waste.

Um, this doesn't match the paper presented at SIGCOMM2000 showing an
uncomfortably high level of errors *above* the TCP layer due to faulty
computer hardware. [1]

The paper strongly recommended application-layer checksums.

Also, your analogy with HTTP is a bit misleading.  iSCSI errors
are cumulative, as disks are usually attached to iSCSI sessions
and a write corruption is stored to disk for later reading.

> can take advantage of 32bit wide data ops.

There is unlikely to be an algorithm better than CRC that is also
faster than CRC.  Essentially, to handle data in words it appears
that you need to trade off the level of protection.  I know of no
paper that explores this assumption, but math is not my strong point.

Glen

 [1] Sorry that I can't give a reference, I'm in the terminal
     room at the Internet2 Joint Techs meeting.

-- 
 Glen Turner                                 Network Engineer
 (08) 8303 3936      Australian Academic and Research Network
 [email-masked]          http://www.aarnet.edu.au/
--
 The revolution will not be televised, it will be digitised

---

From: Sean Quinlan <[email-masked]>
Date: Tue, 30 Jan 2001 22:11:49 -0500

Glen,

> The paper strongly recommended application-layer checksums.

I think the paper you mention is
	"When the CRC and TCP checksum disagree" by Stone and Partridge
http://www.acm.org/sigcomm/sigcomm2000/conf/techprog.htm

I had not seen this paper and it is certainly an interesting read.
One of the conclusions of the paper is that if you really care about
your data you should do a checksum before the data has passed through a
DMA engine.  They mention SSL (also know as TLS) as one way to do this, but
claim that IPsec is not a good option since it is often done by hardware
assist in the NIC after the DMA - Then again if you really believe this, then
you need to do checksums before passing data to your plain old SCSI card, since
this also uses a DMA engine.  This seems a little impractical! Also, considering much
of the iSCSI discussions appears to expect that iSCSI will be offloaded onto
hardware on an adapter card, a checksum at the iSCSI layer would probably
occur after the DMA engine as well...  The paper states
	In the past, one of the authors has periodically recommended
	improving transmission performance by doing the checksum as part of
	the DMA process or in the transmission path in the network interface.
	Base on this study, we can now say that advice is wrong because it
	leave data too exposed to hardware errors.
This sort of goes against the current trend in high speed NICs which provide
offloading of TCP checksums...  My guess is people want the speed provided
such offloading and if the DMA is really causing high error rates, then
the solution is to build more reliable DMA/PCI interfaces.

As a side note, I had a look at the number of TCP checksum errors on
out outside HTTP server and on an internal cpu server.  The outside
machine an error rate of about 1 in 15,000 while the internal machine had
an error rate of 1 in 1,000,000.  Both these numbers where higher than
I would have expected!  Thus, the paper certainly has a point!

> and a write corruption is stored to disk for later reading.

HTTP might not be a perfect analogy, but I believe NFS and CIFS are pretty
close.  I believe almost every byte written to a NetApp server goes
over one of these two protocols and does not include more integrity checking than
provided by TCP and the link layer.

> paper that explores this assumption, but math is not my strong point.

I am certainly no expert in checksums, but I believe the adler32 checksum
used in zip (rfc1950) and SCTP (rfc2960) is claimed to be as good as a 32 bit
CRC and it is more friendly to software implementation on a 32 bit
machine, though given that it uses the mod operator it is more difficult
than CRC to implement in hardware.  Secure hashes such as md5 are also
fairly reasonable in software with the added bonus that they can resist
tampering - these are also probably difficult to do in hardware.

---

From: "Douglas Otis" <[email-masked]>
Date: Wed, 31 Jan 2001 11:19:00 -0800

Sean,

<snip>
> tampering - these are also probably difficult to do in hardware.

I am not sure I understand the difficulty imposed by adler32 with respect to
hardware. Optimal assembly code looks different with about three
instructions per byte. Here is the example implementation of adler32 from
Randall Stewarts reference code (edited):

/* SCTP reference Implementation Copyright (C) 1999 Cisco And Motorola */

ui32
  update_adler32 (ui32 adler, ui8 *buf, ui32 len)
{
  ui32 s1 = adler & 0xffff;
  ui32 s2 = (adler >> 16) & 0xffff;
  i32 n;
  for (n = 0; n < len; n++, buf++)
    {
    s1 = (s1 + *buf);
    if (s1 >= BASE)
	  s1 -= BASE;
    s2 = (s2 + s1);
    if (s2 >= BASE)
	  s2 -= BASE;
    }
return (s2 << 16) + s1;
}

Compliments to Mark Adler (mailto:[email-masked]).

Doug