<?xml version='1.0' encoding='utf-8'?>
<!-- This template is for creating an Internet Draft using xml2rfc,
    which is available here: http://xml.resource.org. -->
<!DOCTYPE rfc SYSTEM "rfc2629-xhtml.ent">
<?xml-stylesheet type='text/xsl' href='rfc2629.xslt' ?>
<?rfc strict="yes" ?>
<?rfc toc="yes"?>
<?rfc tocdepth="4"?>
<?rfc symrefs="yes"?>
<?rfc sortrefs="yes" ?>
<?rfc compact="yes" ?>
<?rfc subcompact="no" ?>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" category="exp" docName="draft-prz-lsr-ash-packets-00" ipr="trust200902" obsoletes="" updates="" submissionType="IETF" xml:lang="en">
  <!-- ***** FRONT MATTER ***** -->

    <front>
    <title>IS-IS Aggregated SNP Hash Packets</title>
    <author initials="T." surname="Przygienda" fullname="Tony Przygienda">
      <organization>HPE Juniper Networking</organization>
      <address>
        <email>antoni.przygienda@hpe.com</email>
      </address>
    </author>
    <author initials="T." surname="Li" fullname="Tony Li">
      <organization>HPE Juniper Networking</organization>
      <address>
        <email>anthony.li@hpe.com</email>
      </address>
    </author>
    <author initials="J." surname="Halpern" fullname="Joel Halpern">
      <organization>HPE Juniper Networking</organization>
      <address>
        <email>joel.halpern@hpe.com</email>
      </address>
    </author>
    <date/>
    <abstract>
      <t>
                The document presents an optional new type of database synchronization packet called an Aggregated
                SNP Hash (ASH). When feasible,
                it compresses
                traditional SNP exchanges into a dynamic Merkle tree-like structure, which speeds up synchronization of large
                databases and adjacency numbers while reducing the load from regular CSNP exchanges during normal
                operation. Just like CSNPs and PSNPs, ASH packets come in two flavors, called Complete ASH (CASH)
                and Partial ASH (PASH).
      </t>
    </abstract>
  </front>
  <middle>
    <section title="Introduction" numbered="true" toc="default">
      <!--            <t>-->
<!--                The document introduces an optional, new type of SNPs called Hierarchical SNP (ASH) that can-->
<!--                compress the traditional CSNPs exchange into a variant of merkle tree, hence-->
<!--                allowing to support fast synchronization of large databases and adjacency numbers while-->
<!--                lessening the load of periodic CSNP exchanges during steady state operation.-->

<!--                Mixture of parallel flooding, exchanging of CSNPs and a fitting ASH compression strategy-->
<!--                should lead to faster database re-synchronization-->
<!--                since only a subset of packets as compared to full scale CSNP exchange-->
<!--                is necessary to reconcile any entropy present. When using ASHs more of unnecessary flooding can be suppressed-->
<!--                while the mismatched fragments will be detected with less communication overhead as compared to-->
<!--                full CSNP exchanges.-->
<!--            </t>-->

            <t>
                The document introduces an optional new type of database synchronization packet called the Aggregated SNP Hash (ASH).
                It compresses and enhances
                traditional CSNP and SNP exchanges with a structure somewhat similar to a
                <xref target="MERKLE" pageno="false" format="default">dynamic Merkle tree</xref>,
                enabling faster synchronization of large
                databases and adjacency information while reducing the overhead of regular CSNP exchanges during
                steady state
                operation.

                By combining parallel flooding, SNP and ASH exchanges, database resynchronization
                can be accelerated because fewer packets (and not the entire CSNP set) are used to fix
                inconsistencies. Using ASHs also reduces unnecessary flooding and communication overhead, while still
                detecting mismatched fragments more efficiently than full CSNP exchanges.
      </t>
      <!--            <t>-->
<!--                To keep the framework uniform we consider-->
<!--                the usual CSNP entries of LSPs simply a "as good as a perfect merkle hash" where we basically use lsp id, seq# and checksum-->
<!--                as "hash" and assume the 16 bits IS-IS fletcher checksum of the content never collides with other fragments. We add the PDU length-->
<!--                of the fragment for further entropy. Subsequently, we build a fletcher checksum on that data as-->
<!--                the fingerprint of the fragment and consider it its hash.-->
<!--                The hash summarizing hashes of all fragments of a node we consider as "node merkle hash". Range of such node merkle-->
<!--                hashes can be again summarized by a hash built over the according hashes.-->
<!--                We will call such hash a "node range merkle hash". For such hashes we do however-->
<!--                move away from fletcher checksums for reasons explained further in <xref target="hashfn"/>.-->

<!--            </t>-->

            <t>
                To maintain the Merkle analogy, we initially treat
                each SNP entry for an LSP as equivalent to an "as good as perfect" -
                though rather long -
                Merkle hash. In other words, the LSP ID, sequence number,
                and checksum act as a "collision-free fragment hash".
                We include the fragment's PDU length in this perfect hash to add
                more entropy in the next step.
                Lifetime is not used since it is
                not compared except in purge cases. The SNP entries then serve as the conceptual "bottom" of
                the Merkle tree.
      </t>
      <t>
                In the following step we compute a <xref target="SIPHASH" pageno="false" format="default"/> over the SNP entry
                data to create a shorter Merkle
                hash for each fragment. Somewhat surprisingly, these per-fragment hashes are never transmitted in packets,
                since doing so would effectively
                duplicate SNP functionality.
                However, the hash that aggregates all fragment hashes of a node becomes a "node Merkle hash". Groups
                of node hashes can then be summarized into a "node range Merkle hash" by combining the
                individual node hashes together. For the node and node range hashes,
                we switch from <xref target="SIPHASH" pageno="false" format="default"/> to a different hashing
                method, as explained in <xref target="hashfn" pageno="false" format="default"/>.
      </t>
      <t>

                The resulting hierarchy of hashes enables large LSDB synchronizations using far fewer
                packets than CSNPs alone. Although the hashes summarize ranges recursively, the actual
                exchange resembles a <xref target="SKIP" pageno="false" format="default"/>-like process rather than
                maintaining a fixed tree structure, since the packets and ranges exchanged vary depending on where
                mismatches are found.
      </t>
      <t>

                This document limits itself to LSDB sizes on the order of 1E6 fragments
                and addresses considerations necessary to prevent overly
                garrulous exchanges
                of hashes covering progressively smaller sets of fragments. More details on the
                targeted IS-IS scale envelope can be found in <xref target="envelope" pageno="false" format="default"/>, and further
                considerations are summarized in
                <xref target="further" pageno="false" format="default"/>.
      </t>
    </section>
    <section title="Example" anchor="startexample" numbered="true" toc="default">
      <t>
                Before diving into precise technical details,
                an example will help introduce the concepts
                involved and demonstrate the behavior of an exchange.
      </t>
      <t> The exchange consists of initial CASH packets (analogous to CSNPs but carrying node range hashes)
                followed by
                PASH packets (analogous to PSNPs) that omit matching ranges and exchange hashes
                over progressively smaller ranges. Once a hash covers a single node only, PSNPs or direct flooding
                is used to synchronize the databases in the traditional IS-IS manner. ASH operates
                at the resolution of a system ID (including its pseudonodes), not individual fragments.
      </t>
      <t>
            The details of packet formats are provided later in <xref target="pash-format" pageno="false" format="default"/> and
            <xref target="cash-format" pageno="false" format="default"/>, and the procedures are defined in <xref target="rules" pageno="false" format="default"/>,
                but the example can be understood intuitively without those details.
                For the sake of readability the example encompasses only a database of roughly 3000 fragments
                over 100 nodes with about 10% of the database not synchronized on start. Details of PSNP packets
                exchanged are omitted for the same reason. Note that the fragment counts shown in the
                examples are informational annotations only and are not carried in the wire format of ASH entries.
      </t>
      <t>Initially, both nodes generate and exchange a CASH packet. Node 1 sends to Node 2 the CASH shown in
                <xref target="n1-cash" pageno="false" format="default"/>, containing
            node range hashes, some of which will disagree with the peer.</t>
      <figure anchor="n1-cash" title="" suppress-title="false" align="left" alt="" width="" height="">
        <artwork align="left" name="" type="" alt="" xml:space="preserve" width="" height=""><![CDATA[
Node1->Node2 (initial CASH) -> 1 CASH packet(s)
Pkt   1 [1010.0000.00.00 - 1010.0063.00.00] (  35 hashes):
1010.0000.00.00 - 1010.0001.FF.FF:   47 frags, hash=$ED6174261B52A218
1010.0002.00.00 - 1010.0002.FF.FF:  150 frags, hash=$BA8EC0FA6F86557A
1010.0003.00.00 - 1010.0006.FF.FF:   87 frags, hash=$2A7D5B4045D54F14
1010.0007.00.00 - 1010.0008.FF.FF:   34 frags, hash=$B15068FC7C4F044B
1010.0009.00.00 - 1010.000A.FF.FF:   85 frags, hash=$4396112BEAB62D20
1010.000B.00.00 - 1010.000E.FF.FF:   88 frags, hash=$CCA15999A8BAA5E8  PEER MISMATCH
1010.000F.00.00 - 1010.0014.FF.FF:   91 frags, hash=$04CA3A21161464F9  PEER MISMATCH
1010.0015.00.00 - 1010.0017.FF.FF:   79 frags, hash=$82039367F94F611C  PEER MISMATCH
1010.0018.00.00 - 1010.001A.FF.FF:   61 frags, hash=$65D4C194A433D2A7
1010.001B.00.00 - 1010.001C.FF.FF:   92 frags, hash=$5F8701B08AE39C2C
1010.001D.00.00 - 1010.001F.FF.FF:   75 frags, hash=$0123DD370E42720C  PEER MISMATCH
1010.0020.00.00 - 1010.0021.FF.FF:   39 frags, hash=$9CFF63C97F8C86D0
1010.0022.00.00 - 1010.0022.FF.FF:  155 frags, hash=$D1513B30C1F1D21D
1010.0023.00.00 - 1010.0026.FF.FF:   91 frags, hash=$51B443F4FE1B20DA  PEER MISMATCH
1010.0027.00.00 - 1010.0029.FF.FF:   83 frags, hash=$44CEC7E1452B1855
1010.002A.00.00 - 1010.002E.FF.FF:   86 frags, hash=$8F1B907E974094F3
1010.002F.00.00 - 1010.0032.FF.FF:   89 frags, hash=$E2A31EC91DB1C4C3  PEER MISMATCH
1010.0033.00.00 - 1010.0036.FF.FF:   85 frags, hash=$E4D0AF41BA0BBDF1  PEER MISMATCH
1010.0037.00.00 - 1010.003B.FF.FF:   82 frags, hash=$13A40B0A950D7694
1010.003C.00.00 - 1010.003C.FF.FF:   23 frags, hash=$DA7335371FB231CD
1010.003D.00.00 - 1010.003D.FF.FF:   99 frags, hash=$D14266119B1003CD
1010.003E.00.00 - 1010.003E.FF.FF:   34 frags, hash=$91D8522BD2EB0523
1010.003F.00.00 - 1010.003F.FF.FF:   91 frags, hash=$9F206FAF8432046E
1010.0040.00.00 - 1010.0040.FF.FF:   88 frags, hash=$79441DDEE60A1F89
1010.0041.00.00 - 1010.0044.FF.FF:   87 frags, hash=$ED7F3899858D61EE
1010.0045.00.00 - 1010.0046.FF.FF:   22 frags, hash=$382CF99A031E42DE
1010.0047.00.00 - 1010.0047.FF.FF:  101 frags, hash=$10B9E7D282B7B8EF
1010.0048.00.00 - 1010.004B.FF.FF:   83 frags, hash=$AC0C721830474E1C
1010.004C.00.00 - 1010.004D.FF.FF:   25 frags, hash=$501B59AE9A65ABC9  PEER MISMATCH
1010.004E.00.00 - 1010.004E.FF.FF:  166 frags, hash=$4EF46668F1DD1A7B
1010.004F.00.00 - 1010.0054.FF.FF:   90 frags, hash=$64362751D60995DA
1010.0055.00.00 - 1010.0059.FF.FF:   87 frags, hash=$2771634B6A78AACE
1010.005A.00.00 - 1010.005C.FF.FF:   91 frags, hash=$E3F65D19CC13C7ED
1010.005D.00.00 - 1010.0060.FF.FF:   84 frags, hash=$C3205BB177BB816E
1010.0061.00.00 - 1010.0063.00.00:   52 frags, hash=$D3500D0875EC4B66
]]></artwork>
      </figure>
      <t>Analogously, Node 2 sends an initial CASH with 33 hashes as shown in <xref target="n2-cash" pageno="false" format="default"/>.
                It may seem counter-intuitive that the ranges do not align, but this is natural since
                the databases themselves differ. <xref target="noderanges" pageno="false" format="default"/> addresses this effect in more
                detail.
      </t>
      <figure anchor="n2-cash" title="" suppress-title="false" align="left" alt="" width="" height="">
        <artwork align="left" name="" type="" alt="" xml:space="preserve" width="" height=""><![CDATA[
Node2->Node1 (initial CASH) -> 1 CASH packet(s)
Pkt   1 [1010.0000.00.00 - 1010.0063.00.00] (  33 hashes):
1010.0000.00.00 - 1010.0001.FF.FF:   47 frags, hash=$ED6174261B52A218
1010.0002.00.00 - 1010.0002.FF.FF:  150 frags, hash=$B312CA61CDDAF08F
1010.0003.00.00 - 1010.0006.FF.FF:   87 frags, hash=$2A7D5B4045D54F14
1010.0007.00.00 - 1010.0008.FF.FF:   34 frags, hash=$B15068FC7C4F044B
1010.0009.00.00 - 1010.000A.FF.FF:   85 frags, hash=$134574F1DF91FBE5
1010.000B.00.00 - 1010.000E.FF.FF:   88 frags, hash=$41DA43916CC90C52  PEER MISMATCH
1010.000F.00.00 - 1010.0014.FF.FF:   91 frags, hash=$866969306096FD29  PEER MISMATCH
1010.0015.00.00 - 1010.0017.FF.FF:   79 frags, hash=$C2CEDFBA88080A3B  PEER MISMATCH
1010.0018.00.00 - 1010.001A.FF.FF:   61 frags, hash=$65D4C194A433D2A7
1010.001B.00.00 - 1010.001C.FF.FF:   92 frags, hash=$0BA9276510EE03CB
1010.001D.00.00 - 1010.0021.FF.FF:   92 frags, hash=$4F5A632B673697BA  PEER MISMATCH
1010.0022.00.00 - 1010.0022.FF.FF:  155 frags, hash=$6EDDFD41D8C4E0A6
1010.0023.00.00 - 1010.0026.FF.FF:   91 frags, hash=$2C2B89EF96A7C7C8  PEER MISMATCH
1010.0027.00.00 - 1010.0029.FF.FF:   83 frags, hash=$878C86D658B81FAD
1010.002A.00.00 - 1010.002E.FF.FF:   86 frags, hash=$8F1B907E974094F3
1010.002F.00.00 - 1010.0034.FF.FF:   81 frags, hash=$2684E1CAA3FB8EB7  PEER MISMATCH
1010.0035.00.00 - 1010.0038.FF.FF:   90 frags, hash=$6D1FAC454620D6BB
1010.0039.00.00 - 1010.003C.FF.FF:   67 frags, hash=$ADB300A6365F6360
1010.003D.00.00 - 1010.003D.FF.FF:   99 frags, hash=$CDF517B289695626
1010.003E.00.00 - 1010.003E.FF.FF:   34 frags, hash=$0365ED0461CA3EDC
1010.003F.00.00 - 1010.003F.FF.FF:   91 frags, hash=$1EA9199FB3A12017
1010.0040.00.00 - 1010.0040.FF.FF:   88 frags, hash=$761D4D297B4BFA45
1010.0041.00.00 - 1010.0044.FF.FF:   87 frags, hash=$1C97513E233AD431
1010.0045.00.00 - 1010.0046.FF.FF:   22 frags, hash=$382CF99A031E42DE
1010.0047.00.00 - 1010.0047.FF.FF:  101 frags, hash=$022CA678714C04D5
1010.0048.00.00 - 1010.004B.FF.FF:   83 frags, hash=$AC0C721830474E1C
1010.004C.00.00 - 1010.004D.FF.FF:   25 frags, hash=$AA9C89D8384E152E  PEER MISMATCH
1010.004E.00.00 - 1010.004E.FF.FF:  166 frags, hash=$D82B8A40F2F6B75D
1010.004F.00.00 - 1010.0054.FF.FF:   90 frags, hash=$64362751D60995DA
1010.0055.00.00 - 1010.0059.FF.FF:   87 frags, hash=$2771634B6A78AACE
1010.005A.00.00 - 1010.005C.FF.FF:   91 frags, hash=$10DAD5C8C452DDCB
1010.005D.00.00 - 1010.0060.FF.FF:   84 frags, hash=$9316E89831EE599B
1010.0061.00.00 - 1010.0063.00.00:   52 frags, hash=$D3500D0875EC4B66
]]></artwork>
      </figure>
      <t>At this point both nodes take the range mismatches they detected and split them
            into smaller ranges. 13 of those splits yield a single system ID as range and
            hence trigger CSNP, PSNP, or full flooding to synchronize the database. This synchronization
            follows standard IS-IS behavior and
            proceeds in parallel, independent of any further ASH exchanges.
            In this example, one additional packet is sent with ranges large enough to warrant ASH, as shown in
            <xref target="n1-pash" pageno="false" format="default"/>.
      </t>
      <figure anchor="n1-pash" title="" suppress-title="false" align="left" alt="" width="" height="">
        <artwork align="left" name="" type="" alt="" xml:space="preserve" width="" height=""><![CDATA[
Node1 -> Node2 (after cuts) -> 1 PASH packet(s)
1010.001D.00.00 - 1010.001F.00.00:   75 frags, hash=$98997F9A286EA1FD  PEER MISMATCH
1010.0020.00.00 - 1010.0021.FF.FF:   39 frags, hash=$9CFF63C97F8C86D0
1010.002F.00.00 - 1010.0031.00.00:   61 frags, hash=$498F595B45397172  PEER MISMATCH
1010.0032.00.00 - 1010.0034.FF.FF:   61 frags, hash=$17E6C25A2881B26E  PEER MISMATCH
]]></artwork>
      </figure>
      <t>
    Splitting the remaining mismatched ranges on Node 2 will lead to CSNP, PSNP, or full flooding for the affected nodes,
    since no ranges large enough to warrant further ASH exchange remain.
</t>
      <t>
                Assuming a PSNP strategy for single-system-ID mismatches, the example generates
                7 PSNPs from both sides (since PSNPs MUST cover all fragments of a system ID
                that ASH discovered to be mismatched), plus 2 CASHes and 3 PASHes.
                This compares favorably to the 62 CSNPs that
                would normally be required on startup.
      </t>
    </section>
    <!--
        <section title="Dynamic Partitioning" anchor="partition">

            <t>
                In practical terms, the most interesting problem
                is figuring out how to divide the database into groups of leaf
                nodes (each representing a set of fragments) that both compress the data effectively compared to
                standard CSNPs and remain as stable as possible. If the fragment boundaries within each hash keep
                changing, neighboring systems will need to recalculate their hashes instead of reusing cached ones from
                their own Merkle tree, which adds unnecessary computation.

                The subdivision should also create enough "bins" to handle any distribution of fragment IDs across the
                network. This helps avoid pathological cases where all fragments might end up in a single hash, such as
                when e.g. a hashing function degrades. Ideally, when a hash mismatch occurs, it should take only one or
                two packets, with hashes for smaller fragment groups or regular CSNPs, to fix the difference.
                Reducing I/O and/or computation directly improves how quickly the systems synchronize.
            </t>

///            <t>
///                Practically speaking, the most interesting problem we encounter is the correct subdivision of the database into
///                collections of leaf nodes (set of fragments contained by such nodes)
///                that on one hand will provide a good compression vs. standard CSNPs,
///                on the other hand it changes as little as possible. In case the boundaries of fragments covered by
///                a hash change all the time, the neighbors receiving the
///                information
///                may have to recompute the hashes rather than relying on a cache representing its own merkle tree of such hashes.
///                The generated subdivision should also produce enough "bins" no matter the distribution of the fragment IDs
///                in the network. This is important to prevent such things as packing of all the fragments into a single hash when
///                e.g. a particular hash function degenerates. And ideally, a hash mismatch should produce not more than a
///                single packet or two with hashes covering less fragments or ultimately, CSNPs.
///                Rather obviously, minimizing necessary I/O and computation will improve re-convergence performance.
///            </t>


            <t>
                To begin, in IS-IS networks we can fit just under 100 SNP entries into the typical 1500-byte MTU frame.
                This is the consequence of each SNP entry including the Node ID, Fragment number, Sequence number, Checksum, and
                Lifetime fields - totaling 7 + 1 + 4 + 2 + 2 = 16 bytes per entry.
            </t>

///            <t>
///                To start our considerations, in IS-IS networks
///                we can fit into the prevailing 1500 bytes somewhat less than 100 of
///                PSNP entries for the foreseeable future. This is the consequence of
///                CSNP entries consuming Node ID + Fragment# + Seq# + CSUM + Lifetime
///                length which
///                amounts to 7 + 1 + 4 + 2 + 2 = 16 bytes each.
///            </t>


///            <t> Subsequently, fletcher hashes will occupy (as shown in <xref target="first-order"/>) the length
///                of 6 + 6 + 6 = 18 bytes
///                per hash
///                and hence around 70 of those hashes fit into a packet. Deeper considerations how the
///                48-bits fletcher checksum and subsequent hashes size has been chosen can be found in
///                <xref target="collisions"/>.
///            </t>

            <t>
                <xref target="SIPHASH"/> hashes, as shown in <xref target="first-order"/>, take up
                6 + 6 + 8 = 20 bytes each, which means that
                about 70 such hashes fit into a single packet. A more detailed explanation of why a 64-bit
                <xref target="SIPHASH"/> and the resulting hash sizes were chosen is provided in <xref target="collisions"/>.
            </t>

            <t>These limits form the basis for the recommended partitioning and packing strategies discussed later.</t>

            <t>
                At the lowest compression level, it is optimal to generate a single SNP packet on a mismatch in
                a hash. To achieve this, the first-level hashes should initially group about 80 LSP fragments
                together, with exceptions handled later. There is no need to maximize this initial packing.
                </t>

            <t>

                As the LSDB grows, it is better to leave some flexibility ("slack") in how fragments are grouped. This
                increases the likelihood that both sides of an adjacency will maintain the same leaf-level packing, even
                during flooding transitions, and prevents the ranges from shifting constantly.


///                At "lowest" compression level, it is desirable to produce one CSNP packet on a miss on the merkle hash
///                and hence such "first level" hashes should  pack initially 80
///                LSP fragments with exceptions following later. There is no need to maximize such "initial packing".
///                LSDB may grow and to
///                maximize the chances of the same "leaf packing" ranges on both sides
///                of an adjacency, even during flooding transitions, some
///                "slack" is advisable so the ranges don't move all the time.
            </t>

            <t>
///                The packing will always put all fragments of a system and its pseudonodes
///                into the same node range (which of course can exceed
///                the advisable 80 fragments sometimes) and a first order leaf will be considered "full" if addition
///                of next System ID fragments would exceed this size (except obviously, when the leaf is empty).
///                At such point the range may be included into a hash of a less specific range which however on
///                mismatch will force disaggregation and sending of less specific ranges.

                The packing process always places all fragments belonging to the same system and its pseudonodes within
                a single node Merkle hash. This hash may occasionally exceed the recommended size of 80 fragments. When
                it comes to node range hashes a
                hash is considered "full" when adding the next system's fragments would go beyond this
                limit. Unless, of course, the leaf is still empty.
                </t>
            <t>

                At that point, any range hash can be folded into a higher-level, less specific range hash. However,
                whenever a mismatch
                occurs at any level, the process must disaggregate the hash and send the corresponding smaller, more
                specific range or node hash instead as defined in <xref target="rules"/>.
            </t>
        
        -->

            <section title="Node Ranges" anchor="noderanges" numbered="true" toc="default">
      <t>To discuss node range hashes meaningfully, we refer to hashes covering a wider range of nodes as
                    "less specific" and those covering only a subset of that range as "more specific".

      </t>
      <!--                <t>-->
<!--                    Instead or in addition to the "first level" packing a system can decide to pack "more densely".-->
<!--                    In such a mode, the ASH may include hashes that cover a much larger range than the-->
<!--                    first-level hashes. How this denser packing is implemented is left up to the specific implementation.-->
<!--                    </t>-->

<!--                <t>-->
<!--                    A good general approach is to increase packing density in parts of the database that have not changed,-->
<!--                    where no hash mismatches have been observed, or when it is reasonable to assume that the-->
<!--                    neighbor already holds a mostly synchronized database.-->
<!--                </t>-->

                <t>



                    When nodes advertise node range hashes to each other, it
                    would obviously be useful for cache-based implementations to agree on which ranges
                    to use, as this would make caching much more
                    effective. However, while this seems viable in theory, implementing it across many
                    interfaces would effectively require a global synchronization protocol - something impractical in a
                    network where nodes constantly add, remove, and update fragments asynchronously. These
                    ongoing changes continually shift what the "optimal" hash ranges are, and with enough churn, such
                    a range negotiation protocol might never converge.

      </t>
      <t>
                    Alternatively, providing a fast way to reconstruct the Merkle hash for a "mismatched" range
                    reduces the need for perfect range alignment. Since hashes are built on system ID boundaries,
                    maintaining a Merkle hash per system ID allows a node to quickly
                    recompute the required hashes whenever received ranges differ from cached ones.

      </t>
      <!--                <t>-->
<!--                    It is still highly preferable for advertised Merkle hash ranges to align on system ID boundaries as-->
<!--                    much as possible - especially at the top level. Under stable conditions, these top-level Merkle hashes-->
<!--                    significantly reduce the amount of CSNP exchange required, minimizing both packet volume and-->
<!--                    processing overhead.-->
<!--                </t>-->

                <t>As a second consideration, even though a fully stable network could in theory be represented by a single hash covering the
                    entire LSDB, doing so is neither desirable nor beneficial. Since an ASH packet must be sent anyway,
                    it is much better to fill it with as many range hashes as possible. This limits the
                    work required when a collision occurs within one of the ranges and also reduces the risk of
                    hash collisions, as discussed in <xref target="collisions" pageno="false" format="default"/>.

      </t>
      <t>
                    In summary, a node should avoid compressing beyond the point where a single ASH covers the entire
                    database. Ideally, one ASH should contain at most about (MTU / Node Range Hash Entry Size)
                    hashes to keep collision probabilities low, as described in <xref target="collisions" pageno="false" format="default"/>.
                    Due to the efficiency of recomputing node range hashes on the fly, there is no need
                    to negotiate or agree on ranges with peers.
      </t>
      <!--
                <t>
                    To attempt to settle on the same ranges in ASHs an implementation of the suggested packing should let a
                    leaf that
                    drops under 50% occupancy "start robbing" system IDs from "left" of the next leaf until the current
                    leaf meets the "full condition". This is of course a recursive action that may ultimately generate
                    less leaves, remove some and in a recursive fashion lead to the same "greedy robbery from the left"
                    in the next level up. The "left" is colloquial here for starting with lowest System IDs under
                    normal sorting criteria.
                    On the other end of the spectrum a leaf that holds more than 150% of usual capacity (i.e. 80 * 1.5 LSPs or 60 * 1.5 Hashes)
                    should be preferably split into two leaves unless it holds a single System ID with more than 80 fragments.
                    Splitting the leaves may cause a repacking at a higher
                    level again in a recursive fashion.

                </t>
Claude (Anthropic) assisted in reviewing the draft specification, identifying ambiguities in zero-hash semantics, overlap handling, and PDU header-to-entry range
 relationships
                <t>
                    The rebalancing of ranges to agree across all nodes and hence reduce hashing load
                    is a trade-off in terms of possibly large recomputation vs. suffering a penalty of
                    recomputing some hashes on disagreeing ranges on every exchange. Other solutions are
                    of course possible such as internal caches that keep the recomputed hashes for the
                    neighbor's ranges.
                </t>

                <t>
                    Precise splitting/merging algorithms agreed upon increase the likelihood of nodes ending up on
                    precisely same ranges. A possibly
                    simpler idea to discuss is to simply "repack" the whole thing on some balance violations or
                    periodically. As example, the optimal dynamic programming algorithm for the problem of
                    optimal partitioning into capacity limited segments is well known.
                    Another idea is to simply use IS-IS fragment sliding but this may lead in worst
                    case to first level checksumming a single fragment over time.
</t>
                <t>
                    Overall, different partitioning and packing approaches are possible but if system ID
                    as natural partition is not used, this will likely change the packet
                    format since the partition boundaries will necessarily reflect which of the fragments are
                    covered by the hashes. Although, given that ordering of fragments has to be preserved it is hard to
                    imagine anything else but start and end consisting of fragment IDs.
                </t>

                -->

            </section>
    <section title="Hash Functions" numbered="true" toc="default">
      <section title="Hash Function for a Fragment" numbered="true" toc="default">
        <t>
                Each fragment generates a 64-bit <xref target="SIPHASH" pageno="false" format="default">siphash-1-3</xref> from
                its full SNP description. The salt key
                is given in <xref target="refcode" pageno="false" format="default"/>.

        </t>
        <t>
                If a fragment hash computes to zero, the value MUST be replaced with
                the constant 1.
        </t>
        <t>
                To validate implementation correctness, a reference hash is provided in
                <xref target="reference-hash" pageno="false" format="default"/>.
        </t>
        <figure anchor="reference-hash" title="" suppress-title="false" align="left" alt="" width="" height="">
          <artwork align="left" name="" type="" alt="" xml:space="preserve" width="" height=""><![CDATA[
Hash Variant: Siphash-1-3:64Bits
Fragment ID: 0101:0101:0000.01-01
Seq# $0001
Csum: $0001
Len: 0512
Hash $6EB348F808C9AE4E
]]></artwork>
        </figure>
      </section>
      <section title="Fast, Incremental, Self-Inverse Hashing Function for Ranges" anchor="hashfn" numbered="true" toc="default">
        <t>
                Since large-scale deployments must compute significant numbers of hashes over sets of frequently changing
                fragments, it is highly desirable to use a specialized hash function that supports fast incremental
                updates when fragments are added, removed, or when their checksums change.
        </t>
        <t>
                Once hashes are built over sets of fragments, it is desirable to support very fast splitting and merging
                of such sets, especially when two hashes differ in which fragments they contain.
        </t>
        <t>
            Further discussion of such hashes can be found in
            <xref target="HASHES" pageno="false" format="default"/>,
            but the design space is
            simplified here since cryptographic security is not a concern.
        </t>
        <t>
                The hash for a set of fragments is computed by XORing their individual fragment hashes. This makes
                it straightforward and very fast
                to update the hash when a fragment is added, removed, or its checksum changes. As a
                result, less specific ranges can quickly derive their hash by XORing the hashes of all included
                more specific ranges, when those are available.

<!--                The hash function for sets of fragments is a very fast XOR operation of their fletcher hashes.-->
<!--                This allows to update the hash when adding, removing a leaf or-->
<!--                changing its checksum-->
<!--                in a very fast and simple manner. This also implies that less specific ranges can very quickly-->
<!--                compute their hash by XOR'ing all included more specific ranges if such hashes are available.-->
        </t>
        <!--
            <t>
                Ultimately, and fairly obviously, third order hash uses second order hash logic to keep its hash.
                This all means that every time a first order leaf changes the
                contained system IDs for
                some reason the merkle hashes will have to be readjusted recursively in according 2nd and third
                order leaves. This is in itself nothing
                particular since
                <em>any</em>
                change on first order leaf hash forces change on second order and consequently third order leaf hash.
                This is how Merkle
                trees work after all.
            </t>
            -->

            <t>
                If a node range hash computes to zero, the value MUST be replaced with
                the constant 1; otherwise the range would assume the semantics of "no fragments present for those nodes".
        </t>
      </section>
    </section>
    <section title="Procedures" numbered="true" toc="default">
      <section title="ASH Support Negotiation" numbered="true" toc="default">
        <t>
                    IIHs of nodes supporting this extension MUST include a new TLV indicating
                    support for reception of ASHes.
                    All nodes on the adjacency MUST advertise
                    this TLV in their IIHs; otherwise ASHes are not used on that adjacency.
                    Note that a node may choose to only receive and process ASHes while always responding
                    with CSNPs or PSNPs, although this is obviously less beneficial than fully supporting
                    both sending and receiving of ASHes.
        </t>
        <t>
                    The ASH Capability TLV is a zero-length TLV (type TBD) included in IIH PDUs.  Its presence
                    indicates that the advertising node is capable of receiving and processing ASH packets.  No
                    value fields are defined at this time; future extensions may add capability flags within
                    this TLV.
        </t>
      </section>
      <section title="Advertising and Receiving CASHes" numbered="true" toc="default">
        <t>
                    Advertising of standard CSNPs is extended or replaced
                    with CASH advertisements when this feature is supported. Analogous to CSNPs,
                    CASHes are sent periodically at the same interval as configured for CSNP advertisement.
                    Since
                    both CSNPs and CASHes carry range information in their headers, they can be freely mixed, depending
                    on which level of fragment "compression" best fits the situation. In practice, sufficiently specific
                    range mismatches will naturally fall back to SNP exchanges or flooding to resolve remaining
                    differences.
<!--                    -->
<!--                    Advertising normal CSNPs is augmented with advertisement of ASHs when this feature is supported.-->
<!--                    Since both CSNP and ASHs advertise "range" in their headers they can even be-->
<!--                    randomly mixed depending what level of "compression" of fragments is best suited to the situation-->
<!--                    and range mismatches specific enough will end up to CSNP exchanges or flooding.-->
        </t>
        <!--                    -->
<!--                    The node sending such an entry SHOULD send according CSNPs before the according ASH packet.-->
<!--                    Observe that in the case the receiving node computed the "real" XOR'ed ASH hash of the fragments of such-->
<!--                    range as resulting in a 0 value, it MUST still treat the received ASH hash as "mismatch" to prevent-->
<!--                    a stable "hole" in the database on the peer.-->


<!--                    Any node IDs not covered by ranges in the packet either due to "holes" between the advertised-->
<!--                    ranges or holes between contained ranges and the Start and End System ID of the ASH MUST be considered-->
<!--                    as missing.-->
<!--                    Consequently, in case a node detects that it holds Merkle hashes for LSPs that are not covered by the-->
<!--                    received ASH, it MUST trigger the same behavior as triggered by CSNP with this condition,-->
<!--                    i.e. flood the missing LSPs.-->


<!--                    -->
<!--                    As is the case with CSNPs the ASH containing in its first range the first node of the-->
<!--                    database MUST indicate in its packet range 0000.0000.0000 as start system ID to-->
<!--                    allow detection of missing nodes. Analogous behavior applies to ASH carrying hash-->
<!--                    covering the last node of the database.-->


<!--                <t>-->
<!--                    When a node receives ASHs with Merkle hash ranges, it MUST either compute-->
<!--                    and verify the hashes for the indicated ranges and in case of mismatch -->
<!--                    send more specific hashes for that range - in other-->
<!--                    words, "disaggregate". Disaggregation is less preferable, however, because if both sides repeatedly-->
<!--                    do this for wide mismatched ranges, it can lead to a ping-pong effect that ultimately falls back to-->
<!--                    full CSNP exchanges.-->
<!--                    -->
<!--                    In case a node receives ASHs where the merkle hash ranges are not the same, the node MUST-->
<!--                    either compute and verify the hashes over the ranges indicated or-->
<!--                    send more specific hashes for the range or in other words "disaggregate". The disaggregation is-->
<!--                    less preferred since in case of  mismatches over wide ranges both sides-->
<!--                    using this strategy end up ultimately in a 'ping-pong' ending with CSNPs.-->
<!--                </t>-->




            </section>
      <section title="Advertising and Receiving PASHes" numbered="true" toc="default">
        <t>Where PSNPs are used, PASHes may extend or substitute the exchange as long as the rules of
                    <xref target="rules" pageno="false" format="default"/> are followed.
        </t>
      </section>
      <section title="Refinement Rules" anchor="rules" numbered="true" toc="default">
        <t>
                ASH exchanges are per-level, analogous to CSNP/PSNP exchanges.  CASH and PASH
                packets are scoped to the IS-IS level (Level 1 or Level 2) of the adjacency on which they
                are sent, just as CSNPs are.
        </t>
        <t>
                Fragments in purge state (remaining lifetime of zero) are NOT included in ASH hash
                computations, consistent with the treatment of purges in CSNP exchanges.  Purges are
                handled through normal IS-IS flooding procedures independently of ASH.
        </t>
        <t>
                When a node receives an ASH where any of the contained hashes
                does not match after recomputation or comparison, it MUST
                 send PASHes with Merkle hashes covering the mismatched ranges. These new hashes MUST be
                more specific than the range where the mismatch occurred.
        </t>
        <t>

                Alternatively, instead of more specific PASH hashes,
                a node MAY choose to send corresponding SNPs
                directly. Sending SNPs immediately may be preferable when the mismatch affects only a
                small number of LSPs.

        </t>
        <t>In case the mismatched hash has no matching fragments in the local
                database, the node MUST fall back to CSNPs or CASHes, or send a PASH with hash set to zero
                to request the remote node to either refine the range or use normal IS-IS procedures
                to synchronize the database.


                    The semantics of a zero hash are consistent in this regard: zero indicates that a node is not present
                    in the system or has no fragments, which for ASH purposes is a distinction without a
                    difference. A zero hash consistently indicates that the range
                    is not covered by ASH compression and MUST be resolved through SNP exchanges or flooding,
                    regardless of whether fragments are actually present.
        </t>
        <!--                <t>-->
            <!--                    -->
            <!--                    A node receiving an ASH where any of the hashes received does not match on recomputation or-->
            <!--                    comparison the result of its own hash-->
            <!--                    MUST send immediately ASHs with Merkle hashes covering the-->
            <!--                    ranges where the hash mismatch was detected. The sent hashes MUST be more specific than-->
            <!--                    the range where the collison occurred.-->
            <!--                    Alternately, a node MAY choose to-->
            <!--                    immediately send according CSNPs, PSNPs or flood the LSPs that have been detected-->
            <!--                    as not matching the merkle hashes. Sending CSNPs or immediate flooding-->
            <!--                    may be preferable if the mismatch-->
            <!--                    covers relatively few LSPs. </t>-->

            <t>
                If there is a mismatch - or no computation available - for a hash covering just a single system ID (including
                its pseudonodes), then SNPs for all fragments of that
                node and its pseudonodes MUST
                be sent, or the fragments flooded directly.
                Since this behavior is initiated from both sides of the adjacency, both nodes
                will ultimately end up with a full set of fragments belonging to the node described by the hash, even
                when using PSNPs.
                Other techniques MAY also be used, such as sending a CSNP covering the range from the smallest
                possible fragment of the system ID to the largest possible fragment.

                <!--                        In case of mismatch or no computation available for a hash covering only a single node-->
                <!--                        (with its pnodes) or mismatch on a hash covering less than a CSNP PDU full of fragments,-->
                <!--                        a CSNP MUST be sent or alternately the node MAY choose to flood those fragments.-->
        </t>
      </section>
    </section>
    <section title="CASH PDU Format" anchor="cash-format" numbered="true" toc="default">
      <t>
<!--                ASH PDU Format follows closely CSNP format where instead of CSNP entries the according merkle-->
<!--                hashes are propagated, i.e. the hashes incorporate strictly the same-->
<!--                fragments that are being incorporated in-->
<!--                CSNP packets. Start and End System IDs do not include pseudo node bytes as those are subsumed-->
<!--                implicitly.-->

                The CASH PDU format closely follows the CSNP format. Instead of CSNP entries, it carries the
                corresponding Merkle hashes - which cover exactly the same fragments that would appear in CSNP packets.
                The Start and End System IDs exclude pseudonode bytes, as those are implicitly included within the
                ranges.
      </t>
      <figure title="" suppress-title="false" align="left" alt="" width="" height="">
        <artwork align="left" name="" type="" alt="" xml:space="preserve" width="" height=""><![CDATA[
                                       No. of Octets
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Intradomain Routing Protocol        |     1
| Discriminator                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length Indicator                    |     1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Version/Protocol ID Extension (1)  |     1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ID Length                           |     1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| R R R   PDU Type                    |     1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Version (1)                         |     1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reserved                            |     1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Max Area Addresses                  |     1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| PDU Length                          |     2
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source ID                           |  ID Length + 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Start System ID                     |  ID Length
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| End System ID                       |  ID Length
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Node Range Hash Entries             |  Variable
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
]]></artwork>
      </figure>
      <t>
                The Start and End System IDs use the standard ID length and indicate the range of fragments covered by
                the ASH, just like CSNPs do. The key difference is that all pseudonodes of the systems within this
                range are implicitly included (implying as well that all fragments of the range are included).
                Both the Start and End System IDs are inclusive, meaning fragments from
                both endpoints are part of the range.  All Node Range Hash Entries
                MUST fall within the bounds defined by the header's Start and End System IDs.


<!--                Start and End System IDs are or the usual ID Length and indicate, just like CSNP do,-->
<!--                the range of fragments that the ASH covers with the difference that all pnodes of-->
<!--                the systems are implied in the range. Start and End System ID are inclusive, i.e.-->
<!--                the fragments of both the start and the end system ID are included.-->
      </t>
      <t>
                The variable length fields are a sorted sequence of Node Range Hash Entries
                in the following format.
      </t>
      <figure title="" suppress-title="false" align="left" alt="" width="" height="">
        <artwork align="left" name="" type="" alt="" xml:space="preserve" width="" height=""><![CDATA[
                                       No. of Octets
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Range Start System ID               |  ID Length
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Range End System ID                 |  ID Length
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Merkle Hash                         |     8
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
]]></artwork>
      </figure>
      <t>The range of fragments included in the hash. Range Start and Range End System IDs are inclusive, i.e.
                fragments of both the start and the end system ID are contained within the range.</t>
      <t>
                The Merkle hash field is 8 octets containing the 64-bit computed hash of all fragments covered
                by the range.
      </t>
      <t>
                With the standard ID Length of 6, each entry is 6 + 6 + 8 = 20 octets, so about 70
                entries fit into a standard 1500-byte MTU.
      </t>
      <t>
                The ranges in a CASH MUST be sorted by Range Start System ID. The ranges MUST NOT overlap.
                A receiver encountering overlapping ranges MUST treat the union of those
                ranges as a single range with a zero hash. Any range exceeding the CASH header bounds
                MUST be clamped to fit within the CASH range and treated as a zero hash.
                Any range whose end is equal to or smaller than its start MUST be discarded.
                A node SHOULD log such events since they most likely indicate
                protocol implementation problems or attacks.
      </t>
      <t>
                Any node IDs that are not covered by the ranges in a CASH - either because there are gaps between
                the advertised ranges, or between those ranges and the ASH's Start and End System IDs - MUST be
                treated as missing. Consequently, if a node detects that it holds Merkle hashes for LSPs that are
                not covered by a received CASH, it MUST behave as it would in the same situation with a CSNP, namely
                by flooding the missing LSPs.
      </t>
      <t>
                As with CSNPs, a CASH whose first range covers the first node in the database MUST use
                0000.0000.0000 as the start system ID in its packet header so that missing nodes can be detected. The
                same rule applies at the other end: a CASH whose range covers the last node MUST
                indicate this so that any missing trailing nodes are detectable.
      </t>
    </section>
    <section title="PASH PDU Format" anchor="pash-format" numbered="true" toc="default">
      <t>


                The PASH PDU format closely follows the CASH format while, analogous to PSNP, omits the range of
                the packet.
      </t>
      <figure title="" suppress-title="false" align="left" alt="" width="" height="">
        <artwork align="left" name="" type="" alt="" xml:space="preserve" width="" height=""><![CDATA[
                                       No. of Octets
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Intradomain Routing Protocol        |     1
| Discriminator                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length Indicator                    |     1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Version/Protocol ID Extension (1)  |     1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ID Length                           |     1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| R R R   PDU Type                    |     1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Version (1)                         |     1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Reserved                            |     1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Max Area Addresses                  |     1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| PDU Length                          |     2
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source ID                           |  ID Length + 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Node Range Hash Entries             |  Variable
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
]]></artwork>
      </figure>
      <t>
                The Variable Length Fields carry the same Node Range Hash Entry format as in CASHes.
      </t>
      <t>PASHes are equivalent to PSNPs in that entries are treated independently:
                gaps between entries have no semantics, entries need not be sorted, and may overlap.
                Any range whose end is equal to or smaller than its start MUST be discarded. A node SHOULD log such events since they most likely indicate
                protocol implementation problems or attacks.
      </t>
    </section>
    <section title="CASH Example" numbered="true" toc="default">
      <t>A more detailed example will clarify CASHes further. Consider an LSDB with nodes having system IDs of
                1000.0000.00&lt;2 digits node-id&gt;, with node-id in the range 0 - 512. Each node
                holds 32 fragments numbered 0 - 31. We assume only nodes
                with even
                identifiers are present, intentionally creating "holes" in the numbering and
                leaving us with 257 nodes.
                The pseudonode byte is treated simply as part of the system ID since it does not affect the
                scheme.

<!--                An example will serve well here. We limit ourselves in the examples to consideration of a-->
<!--                LSDB with 512 nodes with system identifiers of 1000.0000.00 &lt;2 digits node-id&gt; each holding-->
<!--                32 fragments-->
<!--                numbered 0 to 31.-->
<!--                We leave the uneven node identifiers out to have some "holes" in the numbering to hit some corner-->
<!--                cases in further examples.-->
<!--                We disregard the pseudo node byte as simply another byte of system identifier since it does not-->
<!--                contribute-->
<!--                further details to the scheme.-->

      </t>
      <t>
                In a stable state, reasonable compression can deliver 129 "first-order" leaves - each containing fragments
                from 2 systems (64 fragments total) - requiring roughly 129 / 70 ~ 2 packets. The first of these
                "first-order" packets would look approximately like this:


<!--                In a stable state we could expect a reasonable compression-->
<!--                with the following 128 "first order" leaves (each holding 2 systems worth-->
<!--                of fragments, hence 64 fragments) and thus generating 512 / (2 * 70) ~ 4 packets.-->
<!--                First of the  "first order" packets-->
<!--                will look roughly like this-->
      </t>
      <figure anchor="first-order" title="" suppress-title="false" align="left" alt="" width="" height="">
        <artwork align="center" name="" type="" alt="" xml:space="preserve" width="" height=""><![CDATA[
        ...

+--------------------------------------------+
|  Start System ID: 0000.0000.0000           |
+--------------------------------------------+
|  End System ID:   0000.0000.00A0           | // 80 ranges covering 160 nodes
+--------------------------------------------+
+--------------------------------------------+
|  Start System ID: 1000.0000.0000           |
+--------------------------------------------+
|  End System ID:   1000.0000.0002           | // 64 fragments over 2 systems
+--------------------------------------------+
|              Merkle Hash                   |
+--------------------------------------------+
..
|  Start System ID: 1000.0000.008E           |
+--------------------------------------------+
|  End System ID:   1000.0000.00A0           |
+--------------------------------------------+
|              Merkle Hash                   | // 64 fragments over 2 systems
+--------------------------------------------+
]]></artwork>
      </figure>
      <t>
<!--                Based on a local decision a node can start to further compress the ASHs until-->
<!--                in most extreme case it will send just one packet full of hashes. This will divide-->
<!--                in our case 512 nodes across 70 hashes (since all of those have same amount of fragments-->
<!--                for simplicity reason). This ends up being about 8 nodes per hash (equal to 8 * 32 fragments)-->
<!--                and the packet will look as following.-->

                Based on local decisions, a node can further compress ASHs until - in the most extreme case - it sends just
                one packet full of hashes. In our example with 256 nodes, this divides them across 70 hashes (assuming
                equal fragment counts for simplicity), resulting in about 4 nodes per hash (equivalent to 4 * 32
                fragments). The resulting packet would look like this:
      </t>
      <figure anchor="second-order" title="" suppress-title="false" align="left" alt="" width="" height="">
        <artwork align="center" name="" type="" alt="" xml:space="preserve" width="" height=""><![CDATA[
        ...

+--------------------------------------------+
|  Start System ID: 0000.0000.0000           |
+--------------------------------------------+
|  End System ID:   FFFF.FFFF.FFFF           |
+--------------------------------------------+
|  Start System ID: 1000.0000.0000           |
+--------------------------------------------+
|  End System ID:   1000.0000.0010           |
+--------------------------------------------+
|              Merkle Hash                   |
...
+--------------------------------------------+
|  Start System ID: 1000.0000.01F0           |
+--------------------------------------------+
|  End System ID:   1000.0000.0200           |
+--------------------------------------------+
|              Merkle Hash                   |
+--------------------------------------------+
]]></artwork>
      </figure>
    </section>
    <section title="Further Considerations" toc="default" anchor="further" numbered="true">
      <section title="IS-IS Scale Envelope" toc="default" anchor="envelope" numbered="true">
        <!--            <t>-->
<!--                As first, obvious observation, ASHs are of negligible value on small networks of tens of nodes-->
<!--                and hundreds of fragments. Arguably, perfectly correctly implemented flooding is enough at any size (which is-->
<!--                a very optimistic assumption historically speaking) and CSNPs are unnecessary overhead built into-->
<!--                the protocol although practically speaking they contributed largely to the stability of the protocol-->
<!--                deployments over years.-->
<!--                The larger the network becomes the higher the cost of link flap or node restart and the higher-->
<!--                the steady state cost of issuing periodic CSNPs and hence ASH feature can stretch the scale of IS-IS-->
<!--                by a significant degree.-->
<!--                </t>-->

            <t>
                ASHs provide negligible benefit in small networks with only tens of nodes and hundreds of fragments.
                Perfect flooding would theoretically suffice at any scale (though history shows this is too optimistic),
                and CSNPs under such assumptions represent
                protocol overhead only - yet they have significantly contributed to IS-IS stability in real
                deployments.
        </t>
        <t>
                As networks grow larger, the costs of link flaps, node restarts, and periodic CSNP exchanges increase
                substantially. ASHs can significantly extend IS-IS scalability in these scenarios.
        </t>
        <!--            <t>-->
<!--                If we start from the assumption that we want to push IS-IS to its practical limit we realize-->
<!--                that in deployment we have to contend with the limit of what comprises practically viable-->
<!--                flooding rate generated by fragment refreshes and database synchronization.-->
<!--                Path computations still play a role but-->
<!--                they can be much better deferred and take advantage of techniques like dampening and parallelization so-->
<!--                we disregard their impact here unless we deal with pathological cases of 1E6 nodes in a chain-->
<!--                with 1 fragment-->
<!--                each. -->
<!--                Hence, with more realistic assumption of something like 50E3 nodes and 1E6 fragments-->
<!--                we are at maximum configured lifetime in the realms of-->
<!--                1E6/2^16 fragments being refreshed per second and thus generating about 15 packets per-->
<!--                second load per interface (disregarding any flood reduction attempts using techniques like-->
<!--                <xref target="ID.draft-ietf-lsr-distoptflood-11"/>). Additionally, 1E6 fragments generate-->
<!--                about 10E3 CSNP packets on prevalent interface MTUs and thus during LSDB synchronization, to-->
<!--                achieve something in the order of relatively lame 120 seconds LSDB sync up we would have to contend with a-->
<!--                flooding rate of roughly 80 CSNPs per seconds. Multiplied that by an envelope of 16E3 desirable interfaces-->
<!--                we look at a sustained peak flooding rate to the control plane of somewhere around 1.5E6 packets-->
<!--                per second in the system, something that is at least an order of magnitude outside the envelope of any fast flooding-->
<!--                technique and power available for a on-system distributed control plane. Assuming maximum compression-->
<!--                using ASH we are however in the order of one additional packet on top of the flooding caused by-->
<!--                refreshes. This leaves the 15 * 16E3 ~ 2.4E5 flooding rate to be shouldered by reduction and-->
<!--                fast flooding techniques. Not an easy task but within the realm of the possible.-->

            <t>
                To push IS-IS to its practical limits, we must account for flooding rates driven by fragment refreshes
                and LSDB synchronization. Path computation impacts can be deferred - barring pathological scenarios -
                through dampening and parallelization,
                so we focus on realistic scenarios on the order of 50,000 nodes and 1 million fragments. We assume
                an outer envelope of 16K peers, which provides some margin over the largest deployments realized today.
        </t>
        <t>
                At maximum configured lifetime of 65K seconds, this generates ~15 packets/second per interface from refreshes (1M/65K
                fragments/second), plus periodically ~10,000 CSNP packets for full LSDB sync.
        </t>
        <t>
                Achieving sync within a modest 120 seconds
                requires ~80 CSNPs/second, and across 16,000 interfaces this represents a peak of 1.5 million
                packets/second - far
                beyond current fast-flooding capabilities.
                We disregard here techniques like <xref target="ID.draft-ietf-lsr-distoptflood-11" pageno="false" format="default"/>,
                especially since they do not improve CSNP scale.
        </t>
        <t>

                With maximum ASH compression, however, sync overhead drops to roughly one additional packet beyond
                refresh flooding, leaving ~250,000 packets/second (15 * 16K) to be handled by fast flooding and
                flood reduction
                techniques - a challenging but feasible target.
        </t>
        <t>
                The above makes it clear that combining fast flooding, flood reduction, and ASH
                will be essential for extending IS-IS scalability as deployments continue to grow.

<!--                With the above considerations it should become clear that a mixture of fast flooding, flood reduction-->
<!--                and ASH features will be critical to extend the deployable envelope of IS-IS given the ever-increasing-->
<!--                deployment of the protocol.-->
        </t>
      </section>
      <section title="Maximum Advisable Hash Coverage" numbered="true" toc="default">
        <t>
<!--                Although the mechanism can be applied to the point where a single merkle hash represents-->
<!--                an arbitrarily large database, such a single hash is not advisable. Limiting hash coverage-->
<!--                to the point where at maximum a single full ASH packet is far more preferable.-->

                    Although the mechanism can theoretically use a single Merkle hash to represent an arbitrarily large
                    database, such an approach is not advisable. Instead, it is far preferable to limit hash coverage so
                    that at minimum one full ASH packet is required.
        </t>
        <t>
                    In practice, limiting compression so that a maximum of about a dozen ASH packets covers the entire
                    database is usually sufficient. For example, a single maximally compressed ASH packet for a
                    10,000 - fragment database covers ~140 fragments per hash. Allowing for more ASH packets (e.g., 10 instead of
                    100 CSNPs) still provides a 10x compression factor, reduces disaggregation needs during LSDB changes, and
                    further lowers the already negligible collision risk (which in 10K sized LSDB is however
                    vanishingly small with a single hash already).


<!--                    Generally, limiting compression to the point where maximum of a dozen ASH packets cover the database-->
<!--                    can be practically speaking better than enough. As practical example a single ASH packet on-->
<!--                    a 1E3 fragments database compressed to maximum extent-->
<!--                    covers with each hash ~140 fragments. Allowing more ASH packets will lead to less disaggregation-->
<!--                    on LSDB changes obviously, lower collision possibility further (which in 1E3 sized LSDB is however-->
<!--                    vanishingly small with a single hash already) and e.g.-->
<!--                    10 ASH packets compared to 100 CSNP packets covering the LSDB normally-->
<!--                    is a compression factor of 10x already.-->
        </t>
      </section>
      <section title="Hash Collision Probabilities" anchor="collisions" numbered="true" toc="default">
        <!--                <t>-->
<!--                    As first observation it is worth noting that even with CSNPs or PSNPs IS-IS does-->
<!--                    harbor a corner case where LSDB may not end up synchronized, especially on a node restart.-->
<!--                    In simple terms, issuing a fragment with the same sequence number and checksum'ed content that-->
<!--                    collides with a previously sent fragment with a dissimilar content that generates however-->
<!--                    the same 16 bit fletcher checksum will go undetected until the fragment lifetime expires.-->
<!--                    The likelihood of such an event is largely determined by the assumptions as to likelihood-->
<!--                    of a fletcher checksum colliding. A simple assumption we will work from is that values of-->
<!--                    fletcher-->
<!--                    checksums are uniformly likely, even if the content varies by relatively few bytes.-->
<!--                    With that assumption, the-->
<!--                    likelihood of such event is simply 1/2^16 or 0.001%. This will allow for further-->
<!--                    comparisons with ASH fletcher collision likelihood.-->
<!--                    </t>-->
                <t>


                    Even with CSNPs or PSNPs, IS-IS has a corner case where LSDB synchronization can fail - particularly
                    during node restarts. In simple terms, if a new fragment has the same sequence number and
                    different content but an identical 16-bit Fletcher
                    checksum, the collision goes undetected until the fragment expires.
        </t>
        <t>
                    Assuming Fletcher checksums are uniformly distributed (even with minor content changes), the
                    collision probability for that case is 1/2**16 ~ 0.0015%. This baseline enables meaningful
                    comparisons with ASH
                    Fletcher collision probabilities.

        </t>
        <t>
<!--                    ASH generates 48 bit fletcher checksums over what is basically PSNP data of a fragment-->
<!--                    and IS-IS length of the fragment.-->
<!--                    We will be concerned about the likelihood of two fragments being at the same time generating-->
<!--                    the same fletcher checksum while they are covered by the same ASH since such a constellation-->
<!--                    will make both fragments "disappear" due to the nature of the XOR checksum. Any collision-->
<!--                    that is not the in the node range hash is irrelevant.-->

                    ASH uses 64-bit <xref target="SIPHASH" pageno="false" format="default"/> over what is essentially PSNP data for a fragment plus its
                    IS-IS PDU length. The key concern is the probability that two fragments covered by the same
                    hash produce identical hashes simultaneously. This would cause both fragments to
                    "cancel out" due to the XOR nature. Collisions between fragments in
                    different node range hashes are irrelevant.

        </t>
        <t>
                    One might argue that XORing different sets of hashes could produce the same result, but the
                    probability of two distinct sets having identical modulo-1 sums across all 64 bits is vanishingly
                    small. This scenario is not considered further.
        </t>
        <!--                <t>-->
<!--                    One could further argue that XOR'ing several hashes can produce the same value as another-->
<!--                set of XOR'ed hashes but the likelihood of the sets having the same combination of modulo-->
<!--                1 sums on all of the 48 bits is so small (somewhere in the order of 1E-15)-->
<!--                    that we don't consider it further.-->
<!--                </t>-->

                <t>
                    Collision probability analysis is complex for the general case, though the birthday paradox gives a
                    rough estimate of 0.18% collision likelihood for 48-bit hashes in a 1M-sized set. For 64-bit
                    hashes this reduces to ~0.000,002,7%. Rather than
                    relying solely on statistical assumptions, we use extensive simulations that
                    mirror real-world conditions.
</t>
        <t>
                    These simulations model 50,000-node networks with 1M fragments, assuming node IDs differ by only 3
                    bytes, maximum fragment lifetimes, random protocol checksums on fragment refresh, and packet length changes
                    in just 5% of refreshes to reflect network stability. Results are derived from 32 networks running
                    for 2 years each.
        </t>
        <!--                <t>-->
<!--                    Probability statistics in not trivial to deal with the generic case of collisions (though it delivers based on-->
<!--                    birthday paradox first ballpark number of 0.18% collision likelihood in-->
<!--                    48 bit numbers colliding in 1E6 sized set, i.e. a single hash). Intuitively we would expect that using-->
<!--                    64 bit numbers with resulting birthday paradox probability of 0.0000027% would be necessary.-->
<!--                    But with intention of mirroring operational realities as close as possible, we fall on extensive-->
<!--                    simulations of refreshes on a vast network of 50E3 nodes with 1E6 fragments under assumptions-->
<!--                    of node IDs differing in 3 bytes only, maximum fragment lifetime and protocol checksums-->
<!--                    on each sequence number refresh being a random value. We change the length of the packet in-->
<!--                    5% of all refreshes only to mirror a rather stable network. 32 networks, each running for 2 years-->
<!--                    are used to derive the numbers.-->
<!--                </t>-->
                <t>
                    Across 64 years of simulated time and the resulting
                    36E9 fragment refreshes, we observed 142 collisions for the 48-bit variant of
                    <xref target="SIPHASH" pageno="false" format="default"/> across the whole set, or roughly ~0.000,000,4% - much lower than the birthday paradox
                    prediction.
                    However, assuming a single maximum-size
                    ASH packet covering the entire database, only 3 of those collisions matter
                    - a probability of ~0.000,000,008%,
                    or roughly 1 occurrence per 20 years. These collisions have an average lifetime of about 10 hours.
                    The rates are orders of magnitude lower than birthday paradox predictions, likely because node
                    IDs act as a consistent "salt" that effectively pre-partitions the probability space. This is
                    arguably "good enough" by a wide margin.

<!--                    The absolute number of collisions in 36E9 refreshes over set of all fragments-->
<!--                    is in the order of 200-300 collisions or-->
<!--                    6E-9 probability or expressed otherwise about 3 occurrences a year. Lifetime of such a-->
<!--                    collision shows on simulations as average of about 10 hours. The 6E-9 being significantly-->
<!--                    lower than the 1E-3 predicted by birthday paradox is likely to be attributed to the fact-->
<!--                    that node id can be seen as an always differing "salt" to the checksums and hence "pre-partitioning"-->
<!--                    the space of probabilities.-->
        </t>
        <t>
                    Nevertheless, further investigation using the standard 64-bit SipHash 1-3 variant
                    over the same scenario produces *no* detectable collisions. Measuring the CPU
                    cost of the 48-bit variant vs. the 64-bit variant (or even 64-bit traditional Fletcher checksum)
                    shows negligible differences of low single-digit percent with modern implementation
                    techniques. Thus, 64-bit <xref target="SIPHASH" pageno="false" format="default"/> 1-3 has been chosen and should provide
                    a very safe margin even for much larger databases.
        </t>
        <!--                <t>-->
<!--                    The situation becomes more practically relevant when considering collisions within a single-->
<!--                    highest-compression packet containing 70 hashes. This roughly halves the collision rate, leading to-->
<!--                    about 1 collision per year in such a large network. If compression is instead limited to 70 ASH-->
<!--                    packets (rather than maximum compression), the rate drops further to approximately 1 collision per-->
<!--                    10 years.-->
<!--                    </t>-->

<!--                <t>-->
<!--                    The situation becomes more interesting and practically relevant-->
<!--                    once we start to consider how many collision occur in-->
<!--                    the highest compression packet with 70 hashes. This roughly halves the amount of collisions-->
<!--                    so we can expect in such a large network about 1 collision per year. Once we go to the assumption-->
<!--                    that we don't use maximum compression but limit it to 70 ASH packets we end up with about 1 collision-->
<!--                    per 10 years.-->
<!--                </t>-->

<!--                <t>-->
<!--                    Surprisingly, switching to 64-bit hashes reduces total collisions by only about 50%, and under-->
<!--                    maximum compression (single full ASH packet), the results are actually measurably worse. This-->
<!--                    appears to be because 64-bit collisions tend to cluster more closely together in the database - a-->
<!--                    phenomenon we currently lack an explanation for.-->

<!--&lt;!&ndash;                Surprisingly enough, going to 64 bit drops the total number of collisions by about 50% but under the&ndash;&gt;-->
<!--&lt;!&ndash;                assumptions of a single full ASH packet the outcome is measurably worse. This is based on the fact&ndash;&gt;-->
<!--&lt;!&ndash;                taht 64 bit collision hashes occur much "closer together" on the database, something for which we lack an explanation.&ndash;&gt;-->
<!--                </t>-->

                <t>
                    Ultimately, a highly conservative (not to say paranoid) implementation can simply monitor the LSDB for colliding
                    fragment hashes and exclude them from the same ASH hash. This forces receiving
                    nodes to use separate collision-free hashes instead. Such an approach completely eliminates any risk
                    of synchronization misses when using ASHs.
        </t>
        <!--                <t>Obviously, a very, very conservative, not to say paranoid implementation can easily-->
<!--                monitor the LSDB for presence of colliding fletcher hashes on the fragments and simply prevent-->
<!--                such fragments being included in a single ASH hash which will force the receiving node-->
<!--                to consider separate hashes without a collision in them. This is sufficient to prevent-->
<!--                any kind of "misses" when using ASH to synchronize the databases.</t>-->
                <t>
                    Other techniques are possible, such as slowly walking the database and sending CSNPs. However, for a
                    1M-fragment database that generates 10,000 such CSNP packets, the chance of this detecting a collision
                    during its 10-hour window is rather unlikely.
<!--                    -->
<!--                    Other techniques are obviously possible like walking the database at slow speed and sending-->
<!--                    CSNPs. Given a 1E6 fragments database generates 10E3 such packets the chance of this preventing a-->
<!--                    collision during its 10 hours time is probably extremely small.-->
        </t>
      </section>
      <section title="Impact of Packet Losses" numbered="true" toc="default">
        <t>
                    Hashes covering large numbers of fragments are more vulnerable to packet losses, as each lost packet
                    affects a much larger portion of the LSDB during synchronization. Implementations can choose ASH
                    node ranges freely, but should balance maximum compression against "good enough" compression that
                    reduces both collision risk and vulnerability to possible packet drops.

<!--                    Hashes covering large numbers of fragments will be more susceptible to packet losses since each loss-->
<!--                    covers a much larger part of the LSDB during synchronization. An implementation can choose the-->
<!--                    node ranges covered by ASHs in any way it desires but a consideration should be a balance-->
<!--                    between maximum reduction and a compression "good enough" that is less prone to collisions and-->
<!--                    unavoidable packet losses.-->
        </t>
      </section>
      <section title="Decompression and Caching/Comparison Optimizations" numbered="true" toc="default">
        <t>
<!--                    As mentioned above a node may apply many strategies to speed up decompression.-->
<!--                    E.g. LSPs missing in ASHs as not covered by ranges are clearly "missing in action" and can-->
<!--                    be reflooded. As another example,-->
<!--                    small ranges where merkle mismatched can generate CSNPs, PSNPs or lead to flooding immediately.-->

                    As mentioned earlier, nodes can use various strategies to accelerate decompression. For example,
                    LSPs missing from CASHes (those not covered by any ranges) are clearly absent and can be immediately
                    reflooded. Similarly, small mismatched ranges can trigger immediate CSNPs, PSNPs, or direct
                    flooding.

        </t>
        <t>
<!--                    Caching of hashes can be applied at many levels since the merkle hashes suggested here are-->
<!--                    easily computed. Obviously keeping a hash on all fragments of a node and its pnodes is the-->
<!--                    simplest and most relevant candidate but other resolutions are easily achievable.-->
<!--                    Even if certain elements must be removed e.g. on receiving a-->
<!--                    range &lt;A &#45;&#45; B&gt; while the node already holds &lt; A &#45;&#45; B &amp; next-after-B&gt; the hash can be simply-->
<!--                    adjusted by removing 'next-after-B' node merkle hash from the cached result.-->

                    Caching of hashes can be applied at many levels. The
                    simplest and most useful approach is maintaining a hash for all fragments of a node and its
                    pseudonodes, though other granularities work equally well. Even when adjusting for changes - such as
                    receiving a range &lt; A - B &gt; while having cached &lt; A - B &amp; next-after-B &gt;
                    - the cached hash can be quickly updated by
                    simply XORing out the next-after-B node Merkle hash.

        </t>
      </section>
    </section>
    <section title="Security Considerations" toc="default" numbered="true">
      <t>ASH security relies on the same mechanisms protecting IS-IS PDU integrity.
      </t>
    </section>
    <!-- end of security considerations -->

        <section anchor="IGP_IANA" title="IANA Section" numbered="true" toc="default">
      <t>TBD
      </t>
    </section>
    <!-- 2 -->
        <section title="Contributors" toc="default" numbered="true">
      <t>TBD</t>
    </section>
    <!-- end of contributors -->

        <!-- 2 -->
        <section title="Acknowledgement" toc="default" numbered="true">
      <t>

                People have been talking about "compressing CSNPs" for a very long time, reportedly going back to when
                Radia Perlman and an insomniac Dave Katz were walking the halls discussing it. Recent attempts to scale
                the protocol much further have made it worthwhile to turn this idea into a standardized, practical
                engineering solution.
      </t>
      <t>
                Les Ginsberg identified several unresolved issues and contributed alternative ideas to the draft.
      </t>
      <t>
                Job Snijders initiated the discussion of <xref target="SIPHASH" pageno="false" format="default"/> as a likely better solution than
                traditional Fletcher checksumming of fragments.
      </t>
      <t>
                <xref target="CLAUDE" pageno="false" format="default">Claude (Anthropic)</xref> assisted in reviewing the draft specification, identifying
                ambiguities in zero-hash semantics, overlap handling, and PDU header-to-entry range
                relationships.
      </t>
    </section>
    <!-- end of contributors -->

    </middle>
  <back>
    <references title="Informative References">
      <reference anchor="MERKLE" quote-title="true">
        <front>
          <title>A Digital Signature Based on a Conventional Encryption Function</title>
          <author initials="R.C." surname="Merkle">
                    </author>
          <date year="1988"/>
          <keyword>Advances in Cryptology – CRYPTO '87</keyword>
        </front>
      </reference>
      <reference anchor="SIPHASH" target="https://131002.net/siphash/" quote-title="true">
        <front>
          <title>SipHash: A Fast Short-Input PRF</title>
          <author initials="J.-P." surname="Aumasson" fullname="Jean-Philippe Aumasson"/>
          <author initials="D. J." surname="Bernstein" fullname="Daniel J. Bernstein"/>
          <date year="2012"/>
        </front>
        <seriesInfo name="Lecture Notes in Computer Science" value="Vol. 7668, INDOCRYPT 2012, pp. 489-508"/>
      </reference>
      <reference anchor="HASHES" quote-title="true">
        <front>
          <title>Security considerations for incremental hash functions based on pair block chaining</title>
          <author initials="C.-W." surname="Phan">
                    </author>
          <date year="2006"/>
          <keyword>Computers and Security 25</keyword>
        </front>
      </reference>
      <reference anchor="SKIP" quote-title="true">
        <front>
          <title>Skip lists: A probabilistic alternative to balanced trees</title>
          <author initials="W." surname="Pugh">
                    </author>
          <date year="1990"/>
          <keyword>Communications of the ACM</keyword>
        </front>
      </reference>
      <reference anchor="CLAUDE" target="https://www.anthropic.com/claude" quote-title="true">
        <front>
          <title>Claude: AI Assistant</title>
          <author>
            <organization>Anthropic</organization>
          </author>
          <date year="2025"/>
        </front>
      </reference>
      <reference anchor="ID.draft-ietf-lsr-distoptflood-11" quote-title="true">
        <front>
          <title>IS-IS Distributed Flooding Reduction</title>
          <author initials="R." surname="White et al.">
            <organization/>
          </author>
          <date month="Oct" year="2025"/>
        </front>
        <format target="https://www.ietf.org/id/draft-ietf-lsr-distoptflood-11.txt" type="TXT"/>
      </reference>
    </references>
    <!-- end of informative references -->

        <section title="Reference Implementation of SIP Fragment Hashing" anchor="refcode" numbered="true" toc="default">
      <figure title="" suppress-title="false" align="left" alt="" width="" height="">
        <artwork align="center" name="" type="" alt="" xml:space="preserve" width="" height=""><![CDATA[
<CODE BEGINS>
pub fn fragment_hash(
    fragmentid: &SharedFragmentID,
    fragmentcontent: &FragmentContent,
    variant: Option<ASHFragmentHashVariant>,
    size: Option<ASHSize>,
) -> ASHHash {
    let nid = fragmentid.node.node_id().0;
    let pnodebe = fragmentid.pnode.0.to_be_bytes();
    let seqnrbe = fragmentcontent.seqnr.0.to_be_bytes();
    let fragmentnrbe = fragmentid.fragmentnr.0.to_be_bytes();
    let csumbe = fragmentcontent.IS-IS_checksum.0.to_be_bytes();
    let lenbe = fragmentcontent.IS-IS_pdu_length.0.to_be_bytes();

    let mut rotate_in_primary = nid.iter().chain(
        csumbe.iter().chain(
            seqnrbe.iter().chain(
                fragmentnrbe
                    .iter()
                    .chain(lenbe.iter().chain(pnodebe.iter())),
            ),
        ),
    );

    let size = size.unwrap_or(ASHSize::LIBRARY_ASH_SIZE);
    let variant = variant.unwrap_or(ASHFragmentHashVariant::LIBRARY_ASH_FRAGMENT_HASH);

    match variant {
        ASHFragmentHashVariant::Siphash => {

            let key = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16u8];
            let mut hasher = SipHasher13::new_with_key(&key);
            let mut sl = [0u8; 16];
            rotate_in_primary
                .map(|v| *v)
                .collect_slice_checked(&mut sl[..]);
            hasher.write(&sl);
            let r = match hasher.finish() {
                v if v == 0 => 1,
                v if v != 0 => v,
                _ => unreachable!(),
            };
            match size {
                ASHSize::_64Bits => {
                    r.into()
                },
                ASHSize::_48Bits => {
                    let hin = r ^ (r >> 48);
                    (hin & 0xffff_ffff_ffff).into()
                }
<CODE ENDS>
]]></artwork>
      </figure>
    </section>
  </back>
</rfc>
