text: /*
   * Copyright (c) 2017 SUSE LLC.
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at:
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  #ifndef included_vnet_sctp_packet_h
  #define included_vnet_sctp_packet_h
  
  #include <stdbool.h>
  
  #include <vnet/ip/ip4_packet.h>
  #include <vnet/ip/ip6_packet.h>
  
  /*
   * As per RFC 4960
   * https://tools.ietf.org/html/rfc4960
   */
  
  /*
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |     Source Port Number        |     Destination Port Number   |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                      Verification Tag                         |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                           Checksum                            |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef struct
  {
    /*
     * This is the SCTP sender's port number. It can be used by the
     * receiver in combination with the source IP address, the SCTP
     * destination port, and possibly the destination IP address to
     * identify the association to which this packet belongs.
     * The port number 0 MUST NOT be used.
     */
    u16 src_port;
  
    /*
     * This is the SCTP port number to which this packet is destined.
     * The receiving host will use this port number to de-multiplex the
     * SCTP packet to the correct receiving endpoint/application.
     * The port number 0 MUST NOT be used.
     */
    u16 dst_port;
  
    /*
     * The receiver of this packet uses the Verification Tag to validate
     * the sender of this SCTP packet.  On transmit, the value of this
     * Verification Tag MUST be set to the value of the Initiate Tag
     * received from the peer endpoint during the association
     * initialization, with the following exceptions:
     * - A packet containing an INIT chunk MUST have a zero Verification
     *   Tag.
     * - A packet containing a SHUTDOWN COMPLETE chunk with the T bit
     *   set MUST have the Verification Tag copied from the packet with
     *   the SHUTDOWN ACK chunk.
     * - A packet containing an ABORT chunk may have the verification tag
     *   copied from the packet that caused the ABORT to be sent.
     * An INIT chunk MUST be the only chunk in the SCTP packet carrying it.
     */
    u32 verification_tag;
  
    /*
     * This field contains the checksum of this SCTP packet.
     * SCTP uses the CRC32c algorithm.
     */
    u32 checksum;
  
  } sctp_header_t;
  
  always_inline void
  vnet_set_sctp_src_port (sctp_header_t * h, u16 src_port)
  {
    h->src_port = clib_host_to_net_u16 (src_port);
  }
  
  always_inline u16
  vnet_get_sctp_src_port (sctp_header_t * h)
  {
    return (clib_net_to_host_u16 (h->src_port));
  }
  
  always_inline void
  vnet_set_sctp_dst_port (sctp_header_t * h, u16 dst_port)
  {
    h->dst_port = clib_host_to_net_u16 (dst_port);
  }
  
  always_inline u16
  vnet_get_sctp_dst_port (sctp_header_t * h)
  {
    return (clib_net_to_host_u16 (h->dst_port));
  }
  
  always_inline void
  vnet_set_sctp_verification_tag (sctp_header_t * h, u32 verification_tag)
  {
    h->verification_tag = clib_host_to_net_u32 (verification_tag);
  }
  
  always_inline u32
  vnet_get_sctp_verification_tag (sctp_header_t * h)
  {
    return (clib_net_to_host_u32 (h->verification_tag));
  }
  
  always_inline void
  vnet_set_sctp_checksum (sctp_header_t * h, u32 checksum)
  {
    h->checksum = clib_host_to_net_u32 (checksum);
  }
  
  always_inline u32
  vnet_get_sctp_checksum (sctp_header_t * h)
  {
    return (clib_net_to_host_u32 (h->checksum));
  }
  
  /*
   * Multiple chunks can be bundled into one SCTP packet up to the MTU
   * size, except for the INIT, INIT ACK, and SHUTDOWN COMPLETE chunks.
   * These chunks MUST NOT be bundled with any other chunk in a packet.
   *
   *
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                        Common Header                          |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                          Chunk #1                             |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                           ...                                 |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                          Chunk #n                             |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  
  typedef enum
  {
    DATA = 0,
    INIT,
    INIT_ACK,
    SACK,
    HEARTBEAT,
    HEARTBEAT_ACK,
    ABORT,
    SHUTDOWN,
    SHUTDOWN_ACK,
    OPERATION_ERROR,
    COOKIE_ECHO,
    COOKIE_ACK,
    ECNE,
    CWR,
    SHUTDOWN_COMPLETE
  } sctp_chunk_type;
  
  /*
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |   Chunk Type  | Chunk  Flags  |        Chunk Length           |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef struct
  {
    /*
     * This field identifies the type of information contained in the
     * Chunk Value field. It takes a value from 0 to 254.
     * The value of 255 is reserved for future use as an extension field.
     *
     * The values of Chunk Types are defined as follows:
     * ID Value    Chunk Type
     * -----       ----------
     *  0          - Payload Data (DATA)
     *  1          - Initiation (INIT)
     *  2          - Initiation Acknowledgement (INIT ACK)
     *  3          - Selective Acknowledgement (SACK)
     *  4          - Heartbeat Request (HEARTBEAT)
     *  5          - Heartbeat Acknowledgement (HEARTBEAT ACK)
     *  6          - Abort (ABORT)
     *  7          - Shutdown (SHUTDOWN)
     *  8          - Shutdown Acknowledgement (SHUTDOWN ACK)
     *  9          - Operation Error (ERROR)
     *  10         - State Cookie (COOKIE ECHO)
     *  11         - Cookie Acknowledgement (COOKIE ACK)
     *  12         - Reserved for Explicit Congestion Notification Echo (ECNE)
     *  13         - Reserved for Congestion Window Reduced (CWR)
     *  14         - Shutdown Complete (SHUTDOWN COMPLETE)
     *  15 to 62   - available
     *  63         - reserved for IETF-defined Chunk Extensions
     *  64 to 126  - available
     *  127        - reserved for IETF-defined Chunk Extensions
     *  128 to 190 - available
     *  191        - reserved for IETF-defined Chunk Extensions
     *  192 to 254 - available
     *  255        - reserved for IETF-defined Chunk Extensions
     *
     *  Chunk Types are encoded such that the highest-order 2 bits specify
     *  the action that must be taken if the processing endpoint does not
     *  recognize the Chunk Type.
     *  00 -  Stop processing this SCTP packet and discard it, do not
     *  process any further chunks within it.
     *  01 -  Stop processing this SCTP packet and discard it, do not
     *  process any further chunks within it, and report the
     *  unrecognized chunk in an 'Unrecognized Chunk Type'.
     *  10 -  Skip this chunk and continue processing.
     *  11 -  Skip this chunk and continue processing, but report in an
     *  ERROR chunk using the 'Unrecognized Chunk Type' cause of error.
     *
     *  Note: The ECNE and CWR chunk types are reserved for future use of
     *  Explicit Congestion Notification (ECN);
     */
    //u8 type;
  
    /*
     * The usage of these bits depends on the Chunk type as given by the
     * Chunk Type field.  Unless otherwise specified, they are set to 0 on
     * transmit and are ignored on receipt.
     */
    //u8 flags;
  
    /*
     * This value represents the size of the chunk in bytes, including
     * the Chunk Type, Chunk Flags, Chunk Length, and Chunk Value fields.
     * Therefore, if the Chunk Value field is zero-length, the Length
     * field will be set to 4.
     * The Chunk Length field does not count any chunk padding.
     * Chunks (including Type, Length, and Value fields) are padded out
     * by the sender with all zero bytes to be a multiple of 4 bytes
     * long. This padding MUST NOT be more than 3 bytes in total. The
     * Chunk Length value does not include terminating padding of the
     * chunk. However, it does include padding of any variable-length
     * parameter except the last parameter in the chunk. The receiver
     * MUST ignore the padding.
     *
     * Note: A robust implementation should accept the chunk whether or
     * not the final padding has been included in the Chunk Length.
     */
    //u16 length;
  
    u32 params;
  
  } sctp_chunks_common_hdr_t;
  
  typedef struct
  {
    sctp_header_t hdr;
    sctp_chunks_common_hdr_t common_hdr;
  
  } sctp_full_hdr_t;
  
  #define CHUNK_TYPE_MASK 0xFF000000
  #define CHUNK_TYPE_SHIFT 24
  
  #define CHUNK_FLAGS_MASK 0x00FF0000
  #define CHUNK_FLAGS_SHIFT 16
  
  #define CHUNK_LENGTH_MASK 0x0000FFFF
  #define CHUNK_LENGTH_SHIFT 0
  
  always_inline void
  vnet_sctp_common_hdr_params_host_to_net (sctp_chunks_common_hdr_t * h)
  {
    h->params = clib_host_to_net_u32 (h->params);
  }
  
  always_inline void
  vnet_sctp_common_hdr_params_net_to_host (sctp_chunks_common_hdr_t * h)
  {
    h->params = clib_net_to_host_u32 (h->params);
  }
  
  always_inline void
  vnet_sctp_set_chunk_type (sctp_chunks_common_hdr_t * h, sctp_chunk_type t)
  {
    h->params &= ~(CHUNK_TYPE_MASK);
    h->params |= (t << CHUNK_TYPE_SHIFT) & CHUNK_TYPE_MASK;
  }
  
  always_inline u8
  vnet_sctp_get_chunk_type (sctp_chunks_common_hdr_t * h)
  {
    return ((h->params & CHUNK_TYPE_MASK) >> CHUNK_TYPE_SHIFT);
  }
  
  always_inline void
  vnet_sctp_set_chunk_length (sctp_chunks_common_hdr_t * h, u16 length)
  {
    h->params &= ~(CHUNK_LENGTH_MASK);
    h->params |= (length << CHUNK_LENGTH_SHIFT) & CHUNK_LENGTH_MASK;
  }
  
  always_inline u16
  vnet_sctp_get_chunk_length (sctp_chunks_common_hdr_t * h)
  {
    return ((h->params & CHUNK_LENGTH_MASK) >> CHUNK_LENGTH_SHIFT);
  }
  
  /*
   * Payload chunk
   *
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |   Type = 0    | Reserved|U|B|E|    Length                     |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                              TSN                              |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |      Stream Identifier S      |   Stream Sequence Number n    |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                  Payload Protocol Identifier                  |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * \                                                               \
   * /                 User Data (seq n of Stream S)                 /
   * \                                                               \
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef struct
  {
    sctp_header_t sctp_hdr;
    /*
     * Type (8 bits): 0
     * Flags (8 bits):
     * -- Reserved (5 bits): all 0s
     * -- U bit
     * -- B bit
     * -- E bit
     * Length (16 bits): This field indicates the length of the DATA chunk in
     * bytes from the beginning of the type field to the end of the User Data
     * field excluding any padding.
     * A DATA chunk with one byte of user data will have Length set to 17
     * (indicating 17 bytes). A DATA chunk with a User Data field of length L
     * will have the Length field set to (16 + L) (indicating 16+L bytes) where
     * L MUST be greater than 0.
     */
  
    /*
     * Fragment Description Table:
     *
     *    B E                  Description
     * ============================================================
     * |  1 0 | First piece of a fragmented user message          |
     * +----------------------------------------------------------+
     * |  0 0 | Middle piece of a fragmented user message         |
     * +----------------------------------------------------------+
     * |  0 1 | Last piece of a fragmented user message           |
     * +----------------------------------------------------------+
     * |  1 1 | Unfragmented message                              |
     * ============================================================
     */
    sctp_chunks_common_hdr_t chunk_hdr;
  
    /*
     * This value represents the TSN for this DATA chunk.
     * The valid range of TSN is from 0 to 4294967295 (2**32 - 1).
     * TSN wraps back to 0 after reaching 4294967295.
     */
    u32 tsn;
  
    /*
     * Identifies the stream to which the following user data belongs.
     */
    u16 stream_id;
  
    /*
     * This value represents the Stream Sequence Number of the following user data
     * within the stream S. Valid range is 0 to 65535.
     * When a user message is fragmented by SCTP for transport, the same Stream
     * Sequence Number MUST be carried in each of the fragments of the message.
     */
    u16 stream_seq;
  
    /*
     * This value represents an application (or upper layer) specified protocol
     * identifier. This value is passed to SCTP by its upper layer and sent to its
     * peer. This identifier is not used by SCTP but can be used by certain network
     * entities, as well as by the peer application, to identify the type of
     * information being carried in this DATA chunk. This field must be sent even
     * in fragmented DATA chunks (to make sure it is available for agents in the
     * middle of the network).  Note that this field is NOT touched by an SCTP
     * implementation; therefore, its byte order is NOT necessarily big endian.
     * The upper layer is responsible for any byte order conversions to this field.
     * The value 0 indicates that no application identifier is specified by the
     * upper layer for this payload data.
     */
    u32 payload_id;
  
    /*
     * This is the payload user data. The implementation MUST pad the end of the
     * data to a 4-byte boundary with all-zero bytes. Any padding MUST NOT be
     * included in the Length field. A sender MUST never add more than 3 bytes of
     * padding.
     */
    u32 data[];
  
  } sctp_payload_data_chunk_t;
  
  always_inline void
  vnet_sctp_set_ebit (sctp_payload_data_chunk_t * p, u8 enable)
  {
    //p->chunk_hdr.flags = clib_host_to_net_u16 (enable);
  }
  
  always_inline u8
  vnet_sctp_get_ebit (sctp_payload_data_chunk_t * p)
  {
    //return (clib_net_to_host_u16 (p->chunk_hdr.flags));
    return 0;
  }
  
  always_inline void
  vnet_sctp_set_bbit (sctp_payload_data_chunk_t * p, u8 enable)
  {
    //p->chunk_hdr.flags = clib_host_to_net_u16 (enable << 1);
  }
  
  always_inline u8
  vnet_sctp_get_bbit (sctp_payload_data_chunk_t * p)
  {
    //return (clib_net_to_host_u16 (p->chunk_hdr.flags >> 1));
    return 0;
  }
  
  always_inline void
  vnet_sctp_set_ubit (sctp_payload_data_chunk_t * p, u8 enable)
  {
    //p->chunk_hdr.flags = clib_host_to_net_u16 (enable << 2);
  }
  
  always_inline u8
  vnet_sctp_get_ubit (sctp_payload_data_chunk_t * p)
  {
    //return (clib_net_to_host_u16 (p->chunk_hdr.flags >> 2));
    return 0;
  }
  
  always_inline void
  vnet_sctp_set_tsn (sctp_payload_data_chunk_t * p, u32 tsn)
  {
    p->tsn = clib_host_to_net_u32 (tsn);
  }
  
  always_inline u32
  vnet_sctp_get_tsn (sctp_payload_data_chunk_t * p)
  {
    return (clib_net_to_host_u32 (p->tsn));
  }
  
  always_inline void
  vnet_sctp_set_stream_id (sctp_payload_data_chunk_t * p, u16 stream_id)
  {
    p->stream_id = clib_host_to_net_u16 (stream_id);
  }
  
  always_inline u16
  vnet_sctp_get_stream_id (sctp_payload_data_chunk_t * p)
  {
    return (clib_net_to_host_u16 (p->stream_id));
  }
  
  always_inline void
  vnet_sctp_set_stream_seq (sctp_payload_data_chunk_t * p, u16 stream_seq)
  {
    p->stream_seq = clib_host_to_net_u16 (stream_seq);
  }
  
  always_inline u16
  vnet_sctp_get_stream_seq (sctp_payload_data_chunk_t * p)
  {
    return (clib_net_to_host_u16 (p->stream_seq));
  }
  
  always_inline void
  vnet_sctp_set_payload_id (sctp_payload_data_chunk_t * p, u32 payload_id)
  {
    p->payload_id = clib_host_to_net_u32 (payload_id);
  }
  
  always_inline u32
  vnet_sctp_get_payload_id (sctp_payload_data_chunk_t * p)
  {
    return (clib_net_to_host_u32 (p->payload_id));
  }
  
  always_inline u16
  vnet_sctp_calculate_padding (u16 base_length)
  {
    if (base_length % 4 == 0)
      return 0;
  
    return (4 - base_length % 4);
  }
  
  #define DEFAULT_A_RWND 1480
  #define INBOUND_STREAMS_COUNT 1
  #define OUTBOUND_STREAMS_COUNT 1
  
  /*
   * INIT chunk
   *
   * This chunk is used to initiate an SCTP association between two
   * endpoints.
   *
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |   Type = 1    |  Chunk Flags  |      Chunk Length             |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                         Initiate Tag                          |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |           Advertised Receiver Window Credit (a_rwnd)          |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |  Number of Outbound Streams   |  Number of Inbound Streams    |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                          Initial TSN                          |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * \                                                               \
   * /              Optional/Variable-Length Parameters              /
   * \                                                               \
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   *
   * The INIT chunk contains the following parameters. Unless otherwise
   * noted, each parameter MUST only be included once in the INIT chunk.
   *
   * Fixed Parameters                     Status
   * ----------------------------------------------
   *  Initiate Tag                        Mandatory
   *  Advertised Receiver Window Credit   Mandatory
   *  Number of Outbound Streams          Mandatory
   *  Number of Inbound Streams           Mandatory
   *  Initial TSN                         Mandatory
   *
   * Variable Parameters                  Status     Type Value
   * -------------------------------------------------------------
   *  IPv4 Address (Note 1)               Optional    5
   *  IPv6 Address (Note 1)               Optional    6
   *  Cookie Preservative			Optional    9
   *  Reserved for ECN Capable (Note 2)   Optional    32768 (0x8000)
   *  Host Name Address (Note 3)          Optional    11
   *  Supported Address Types (Note 4)    Optional    12
   *
   * Note 1: The INIT chunks can contain multiple addresses that can be
   * IPv4 and/or IPv6 in any combination.
   *
   * Note 2: The ECN Capable field is reserved for future use of Explicit
   * Congestion Notification.
   *
   * Note 3: An INIT chunk MUST NOT contain more than one Host Name Address
   * parameter. Moreover, the sender of the INIT MUST NOT combine any other
   * address types with the Host Name Address in the INIT. The receiver of
   * INIT MUST ignore any other address types if the Host Name Address parameter
   * is present in the received INIT chunk.
   *
   * Note 4: This parameter, when present, specifies all the address types the
   * sending endpoint can support.  The absence of this parameter indicates that
   * the sending endpoint can support any address type.
   *
   * IMPLEMENTATION NOTE: If an INIT chunk is received with known parameters that
   * are not optional parameters of the INIT chunk, then the receiver SHOULD
   * process the INIT chunk and send back an INIT ACK. The receiver of the INIT
   * chunk MAY bundle an ERROR chunk with the COOKIE ACK chunk later.
   * However, restrictive implementations MAY send back an ABORT chunk in response
   * to the INIT chunk. The Chunk Flags field in INIT is reserved, and all bits
   * in it should be set to 0 by the sender and ignored by the receiver.
   * The sequence of parameters within an INIT can be processed in any order.
   */
  typedef struct
  {
    sctp_header_t sctp_hdr;
    sctp_chunks_common_hdr_t chunk_hdr;
  
    /*
     * The receiver of the INIT (the responding end) records the value of
     * the Initiate Tag parameter.
     * This value MUST be placed into the Verification Tag field of every
     * SCTP packet that the receiver of the INIT transmits within this association.
     * The Initiate Tag is allowed to have any value except 0.
     *
     * If the value of the Initiate Tag in a received INIT chunk is found
     * to be 0, the receiver MUST treat it as an error and close the
     * association by transmitting an ABORT.
     *
     * The value of the INIT TAG is recommended to be random for security
     * reasons. A good method is described in https://tools.ietf.org/html/rfc4086
     */
    u32 initiate_tag;
  
    /*
     * This value represents the dedicated buffer space, in number of bytes,
     * the sender of the INIT has reserved in association with this window.
     * During the life of the association, this buffer space SHOULD NOT be
     * lessened (i.e., dedicated buffers taken away from this association);
     * however, an endpoint MAY change the value of a_rwnd it sends in SACK
     * chunks.
     */
    u32 a_rwnd;
  
    /*
     * Defines the number of outbound streams the sender of this INIT chunk
     * wishes to create in this association.
     * The value of 0 MUST NOT be used.
     *
     * Note: A receiver of an INIT with the OS value set to 0 SHOULD abort
     * the association.
     */
    u16 outbound_streams_count;
  
    /*
     * Defines the maximum number of streams the sender of this INIT
     * chunk allows the peer end to create in this association.
     * The value 0 MUST NOT be used.
     *
     * Note: There is no negotiation of the actual number of streams but
     * instead the two endpoints will use the min(requested, offered).
     *
     * Note: A receiver of an INIT with the MIS value of 0 SHOULD abort
     * the association.
     */
    u16 inboud_streams_count;
  
    /*
     * Defines the initial TSN that the sender will use.
     * The valid range is from 0 to 4294967295.
     * This field MAY be set to the value of the Initiate Tag field.
     */
    u32 initial_tsn;
  
    /* The following field allows to have multiple optional fields which are:
     * - sctp_ipv4_address
     * - sctp_ipv6_address
     * - sctp_cookie_preservative
     * - sctp_hostname_address
     * - sctp_supported_address_types
     */
    u32 optional_fields[];
  
  } sctp_init_chunk_t;
  
  /*
   * INIT ACK chunk
   *
   * The INIT ACK chunk is used to acknowledge the initiation of an SCTP
   * association. The parameter part of INIT ACK is formatted similarly to the
   * INIT chunk.
   *
   * It uses two extra variable parameters:
   * - the State Cookie and
   * - the Unrecognized Parameter:
   */
  /*
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |   Type = 2    |  Chunk Flags  |      Chunk Length             |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                         Initiate Tag                          |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |              Advertised Receiver Window Credit                |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |  Number of Outbound Streams   |  Number of Inbound Streams    |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                          Initial TSN                          |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * \                                                               \
   * /              Optional/Variable-Length Parameters              /
   * \                                                               \
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef sctp_init_chunk_t sctp_init_ack_chunk_t;
  
  /*
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |          Parameter Type       |       Parameter Length        |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * \                                                               \
   * /                       Parameter Value                         /
   * \                                                               \
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef struct
  {
    u16 type;
    u16 length;
  
  } sctp_opt_params_hdr_t;
  
  typedef struct
  {
    sctp_opt_params_hdr_t param_hdr;
  
    u64 mac;			/* RFC 2104 */
    u32 creation_time;
    u32 cookie_lifespan;
  
  } sctp_state_cookie_param_t;
  
  /*
   *  This chunk is used only during the initialization of an association.
   *  It is sent by the initiator of an association to its peer to complete
   *  the initialization process.  This chunk MUST precede any DATA chunk
   *  sent within the association, but MAY be bundled with one or more DATA
   *  chunks in the same packet.
   *
   *  0                   1                   2                   3
   *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   *  |   Type = 10   |Chunk  Flags   |         Length                |
   *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   *  /                     Cookie                                    /
   *  \                                                               \
   *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef struct
  {
    sctp_header_t sctp_hdr;
    sctp_chunks_common_hdr_t chunk_hdr;
  
    sctp_state_cookie_param_t cookie;
  
  } sctp_cookie_echo_chunk_t;
  
  
  /*
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |   Type = 11   |Chunk  Flags   |     Length = 4                |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef struct
  {
    sctp_header_t sctp_hdr;
    sctp_chunks_common_hdr_t chunk_hdr;
  
  } sctp_cookie_ack_chunk_t;
  
  /*
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |   Type = 14   |Chunk  Flags   |     Length = 4                |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef struct
  {
    sctp_header_t sctp_hdr;
    sctp_chunks_common_hdr_t chunk_hdr;
  
  } sctp_shutdown_complete_chunk_t;
  
  /* OPTIONAL or VARIABLE-LENGTH parameters for INIT */
  #define SCTP_IPV4_ADDRESS_TYPE	5
  #define SCTP_IPV4_ADDRESS_TYPE_LENGTH 8
  #define SCTP_IPV6_ADDRESS_TYPE	6
  #define SCTP_IPV6_ADDRESS_TYPE_LENGTH 20
  #define SCTP_STATE_COOKIE_TYPE		7
  #define SCTP_UNRECOGNIZED_TYPE	8
  #define SCTP_COOKIE_PRESERVATIVE_TYPE	9
  #define SCTP_COOKIE_PRESERVATIVE_TYPE_LENGTH	8
  #define SCTP_HOSTNAME_ADDRESS_TYPE 	11
  #define SCTP_SUPPORTED_ADDRESS_TYPES	12
  
  /*
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |        Type = 5               |      Length = 8               |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                        IPv4 Address                           |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef struct
  {
    sctp_opt_params_hdr_t param_hdr;
  
    /*
     * Contains an IPv4 address of the sending endpoint.
     * It is binary encoded.
     */
    ip4_address_t address;
  
  } sctp_ipv4_addr_param_t;
  
  always_inline void
  vnet_sctp_set_ipv4_address (sctp_ipv4_addr_param_t * a, ip4_address_t address)
  {
    a->param_hdr.type = clib_host_to_net_u16 (SCTP_IPV4_ADDRESS_TYPE);
    a->param_hdr.length = clib_host_to_net_u16 (8);
    a->address.as_u32 = clib_host_to_net_u32 (address.as_u32);
  }
  
  always_inline u32
  vnet_sctp_get_ipv4_address (sctp_ipv4_addr_param_t * a)
  {
    return (clib_net_to_host_u32 (a->address.as_u32));
  }
  
  /*
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |            Type = 6           |          Length = 20          |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                                                               |
   * |                         IPv6 Address                          |
   * |                                                               |
   * |                                                               |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef struct
  {
    sctp_opt_params_hdr_t param_hdr;
  
    /*
     * Contains an IPv6 address of the sending endpoint.
     * It is binary encoded.
     */
    ip6_address_t address;
  
  } sctp_ipv6_addr_param_t;
  
  always_inline void
  vnet_sctp_set_ipv6_address (sctp_ipv6_addr_param_t * a, ip6_address_t address)
  {
    a->param_hdr.type = clib_host_to_net_u16 (SCTP_IPV6_ADDRESS_TYPE);
    a->param_hdr.length = clib_host_to_net_u16 (20);
    a->address.as_u64[0] = clib_host_to_net_u64 (address.as_u64[0]);
    a->address.as_u64[1] = clib_host_to_net_u64 (address.as_u64[1]);
  }
  
  always_inline ip6_address_t
  vnet_sctp_get_ipv6_address (sctp_ipv6_addr_param_t * a)
  {
    ip6_address_t ip6_address;
  
    ip6_address.as_u64[0] = clib_net_to_host_u64 (a->address.as_u64[0]);
    ip6_address.as_u64[1] = clib_net_to_host_u64 (a->address.as_u64[1]);
  
    return ip6_address;
  }
  
  /*
   * The sender of the INIT shall use this parameter to suggest to the
   * receiver of the INIT for a longer life-span of the State Cookie.
   */
  /*
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |          Type = 9             |          Length = 8           |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |         Suggested Cookie Life-Span Increment (msec.)          |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef struct
  {
    sctp_opt_params_hdr_t param_hdr;
  
    /*
     * This parameter indicates to the receiver how much increment in
     * milliseconds the sender wishes the receiver to add to its default
     * cookie life-span.
     *
     * This optional parameter should be added to the INIT chunk by the
     * sender when it reattempts establishing an association with a peer
     * to which its previous attempt of establishing the association
     * failed due to a stale cookie operation error. The receiver MAY
     * choose to ignore the suggested cookie life-span increase for its
     * own security reasons.
     */
    u32 life_span_inc;
  
  } sctp_cookie_preservative_param_t;
  
  always_inline void
  vnet_sctp_set_cookie_preservative (sctp_cookie_preservative_param_t * c,
  				   u32 life_span_inc)
  {
    c->param_hdr.type = clib_host_to_net_u16 (SCTP_COOKIE_PRESERVATIVE_TYPE);
    c->param_hdr.length = clib_host_to_net_u16 (8);
    c->life_span_inc = clib_host_to_net_u32 (life_span_inc);
  }
  
  always_inline u32
  vnet_sctp_get_cookie_preservative (sctp_cookie_preservative_param_t * c)
  {
    return (clib_net_to_host_u32 (c->life_span_inc));
  }
  
  #define FQDN_MAX_LENGTH 256
  
  /*
   * The sender of INIT uses this parameter to pass its Host Name (in
   * place of its IP addresses) to its peer.
   * The peer is responsible for resolving the name.
   * Using this parameter might make it more likely for the association to work
   * across a NAT box.
   */
  /*
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |          Type = 11            |          Length               |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * /                          Host Name                            /
   * \                                                               \
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef struct
  {
    sctp_opt_params_hdr_t param_hdr;
  
  
    /*
     * This field contains a host name in "host name syntax" per RFC 1123
     * Section 2.1
     *
     * Note: At least one null terminator is included in the Host Name
     * string and must be included in the length.
     */
    char hostname[FQDN_MAX_LENGTH];
  
  } sctp_hostname_param_t;
  
  always_inline void
  vnet_sctp_set_hostname_address (sctp_hostname_param_t * h, char *hostname)
  {
    h->param_hdr.length = FQDN_MAX_LENGTH;
    h->param_hdr.type = clib_host_to_net_u16 (SCTP_HOSTNAME_ADDRESS_TYPE);
    memset (h->hostname, '0', FQDN_MAX_LENGTH);
    memcpy (h->hostname, hostname, FQDN_MAX_LENGTH);
  }
  
  #define MAX_SUPPORTED_ADDRESS_TYPES	3
  
  /*
   * The sender of INIT uses this parameter to list all the address types
   * it can support.
   */
  /*
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |          Type = 12            |          Length               |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |        Address Type #1        |        Address Type #2        |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                            ......                             |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+
   */
  typedef struct
  {
    sctp_opt_params_hdr_t param_hdr;
  
    u16 address_type[MAX_SUPPORTED_ADDRESS_TYPES];
  
  } sctp_supported_addr_types_param_t;
  
  always_inline void
  vnet_sctp_set_supported_address_types (sctp_supported_addr_types_param_t * s)
  {
    s->param_hdr.type = clib_host_to_net_u16 (SCTP_SUPPORTED_ADDRESS_TYPES);
    s->param_hdr.length = 4 /* base = type + length */  +
      MAX_SUPPORTED_ADDRESS_TYPES * 4;	/* each address type is 4 bytes */
  
    s->address_type[0] = clib_host_to_net_u16 (SCTP_IPV4_ADDRESS_TYPE);
    s->address_type[1] = clib_host_to_net_u16 (SCTP_IPV6_ADDRESS_TYPE);
    s->address_type[2] = clib_host_to_net_u16 (SCTP_HOSTNAME_ADDRESS_TYPE);
  }
  
  /*
   * Error cause codes to be used for the sctp_error_cause.cause_code field
   */
  #define INVALID_STREAM_IDENTIFIER	1
  #define MISSING_MANDATORY_PARAMETER	2
  #define STALE_COOKIE_ERROR		3
  #define OUT_OF_RESOURCE			4
  #define UNRESOLVABLE_ADDRESS		5
  #define UNRECOGNIZED_CHUNK_TYPE		6
  #define INVALID_MANDATORY_PARAMETER	7
  #define UNRECOGNIZED_PARAMETER		8
  #define NO_USER_DATA			9
  #define COOKIE_RECEIVED_WHILE_SHUTTING_DOWN	10
  #define RESTART_OF_ASSOCIATION_WITH_NEW_ADDR	11
  #define USER_INITIATED_ABORT		12
  #define PROTOCOL_VIOLATION		13
  
  always_inline void
  vnet_sctp_set_state_cookie (sctp_state_cookie_param_t * s)
  {
    s->param_hdr.type = clib_host_to_net_u16 (SCTP_STATE_COOKIE_TYPE);
  
    /* TODO: length & value to be populated */
  }
  
  typedef struct
  {
    sctp_opt_params_hdr_t param_hdr;
  
    u32 value[];
  
  } sctp_unrecognized_param_t;
  
  always_inline void
  vnet_sctp_set_unrecognized_param (sctp_unrecognized_param_t * u)
  {
    u->param_hdr.type = clib_host_to_net_u16 (UNRECOGNIZED_PARAMETER);
  
    /* TODO: length & value to be populated */
  }
  
  /*
   * Selective ACK (SACK) chunk
   *
   * This chunk is sent to the peer endpoint to acknowledge received DATA
   * chunks and to inform the peer endpoint of gaps in the received
   * subsequences of DATA chunks as represented by their TSNs.
   */
  /*
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |   Type = 3    |Chunk  Flags   |      Chunk Length             |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                      Cumulative TSN Ack                       |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |          Advertised Receiver Window Credit (a_rwnd)           |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * | Number of Gap Ack Blocks = N  |  Number of Duplicate TSNs = X |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |  Gap Ack Block #1 Start       |   Gap Ack Block #1 End        |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * /                                                               /
   * \                              ...                              \
   * /                                                               /
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |   Gap Ack Block #N Start      |  Gap Ack Block #N End         |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                       Duplicate TSN 1                         |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * /                                                               /
   * \                              ...                              \
   * /                                                               /
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                       Duplicate TSN X                         |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef struct
  {
    sctp_header_t sctp_hdr;
    sctp_chunks_common_hdr_t chunk_hdr;
    /*
     * This parameter contains the TSN of the last DATA chunk received in
     * sequence before a gap.  In the case where no DATA chunk has been
     * received, this value is set to the peer's Initial TSN minus one.
     */
    u32 cumulative_tsn_ack;
  
    /*
     * This field indicates the updated receive buffer space in bytes of
     * the sender of this SACK.
     */
    u32 a_rwnd;
  
    /*
     * Indicates the number of Gap Ack Blocks included in this SACK.
     */
    u16 gap_ack_blocks_count;
  
    /*
     * This field contains the number of duplicate TSNs the endpoint has
     * received.  Each duplicate TSN is listed following the Gap Ack Block
     * list.
     */
    u16 duplicate_tsn_count;
  
    /*
     * Indicates the Start offset TSN for this Gap Ack Block. To calculate
     * the actual TSN number the Cumulative TSN Ack is added to this offset
     * number. This calculated TSN identifies the first TSN in this Gap Ack
     * Block that has been received.
     */
    u16 *gap_ack_block_start;
  
    /*
     * Indicates the End offset TSN for this Gap Ack Block. To calculate
     * the actual TSN number, the Cumulative TSN Ack is added to this offset
     * number. This calculated TSN identifies the TSN of the last DATA chunk
     * received in this Gap Ack Block.
     */
    u16 *gap_ack_block_end;
  
    /*
     * Indicates the number of times a TSN was received in duplicate since
     * the last SACK was sent. Every time a receiver gets a duplicate TSN
     * (before sending the SACK), it adds it to the list of duplicates.
     * The duplicate count is reinitialized to zero after sending each SACK.
     */
    u32 duplicate_tsn;
  
  } sctp_selective_ack_chunk_t;
  
  always_inline void
  vnet_sctp_set_cumulative_tsn_ack (sctp_selective_ack_chunk_t * s,
  				  u32 cumulative_tsn_ack)
  {
    vnet_sctp_set_chunk_type (&s->chunk_hdr, SACK);
    s->cumulative_tsn_ack = clib_host_to_net_u32 (cumulative_tsn_ack);
  }
  
  always_inline u32
  vnet_sctp_get_cumulative_tsn_ack (sctp_selective_ack_chunk_t * s)
  {
    return clib_net_to_host_u32 (s->cumulative_tsn_ack);
  }
  
  always_inline void
  vnet_sctp_set_arwnd (sctp_selective_ack_chunk_t * s, u32 a_rwnd)
  {
    vnet_sctp_set_chunk_type (&s->chunk_hdr, SACK);
    s->a_rwnd = clib_host_to_net_u32 (a_rwnd);
  }
  
  always_inline u32
  vnet_sctp_get_arwnd (sctp_selective_ack_chunk_t * s)
  {
    return clib_net_to_host_u32 (s->a_rwnd);
  }
  
  always_inline void
  vnet_sctp_set_gap_ack_blocks_count (sctp_selective_ack_chunk_t * s,
  				    u16 gap_ack_blocks_count)
  {
    vnet_sctp_set_chunk_type (&s->chunk_hdr, SACK);
    s->gap_ack_blocks_count = clib_host_to_net_u16 (gap_ack_blocks_count);
  
    if (s->gap_ack_block_start == NULL)
      s->gap_ack_block_start =
        clib_mem_alloc (sizeof (u16) * gap_ack_blocks_count);
    if (s->gap_ack_block_end == NULL)
      s->gap_ack_block_end =
        clib_mem_alloc (sizeof (u16) * gap_ack_blocks_count);
  }
  
  always_inline u16
  vnet_sctp_get_gap_ack_blocks_count (sctp_selective_ack_chunk_t * s)
  {
    return clib_net_to_host_u32 (s->gap_ack_blocks_count);
  }
  
  always_inline void
  vnet_sctp_set_duplicate_tsn_count (sctp_selective_ack_chunk_t * s,
  				   u16 duplicate_tsn_count)
  {
    vnet_sctp_set_chunk_type (&s->chunk_hdr, SACK);
    s->duplicate_tsn_count = clib_host_to_net_u16 (duplicate_tsn_count);
  }
  
  always_inline u16
  vnet_sctp_get_duplicate_tsn_count (sctp_selective_ack_chunk_t * s)
  {
    return clib_net_to_host_u16 (s->duplicate_tsn_count);
  }
  
  /*
   * Heartbeat Info
   *
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |    Heartbeat Info Type=1      |         HB Info Length        |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * /                  Sender-Specific Heartbeat Info               /
   * \                                                               \
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef struct
  {
    sctp_opt_params_hdr_t param_hdr;
  
    /*
     * The Sender-Specific Heartbeat Info field should normally include
     * information about the sender's current time when this HEARTBEAT
     * chunk is sent and the destination transport address to which this
     * HEARTBEAT is sent.
     * This information is simply reflected back by the receiver in the
     * HEARTBEAT ACK message.
     *
     * Note also that the HEARTBEAT message is both for reachability
     * checking and for path verification.
     * When a HEARTBEAT chunk is being used for path verification purposes,
     * it MUST hold a 64-bit random nonce.
     */
    u64 hb_info;
  
  } sctp_hb_info_param_t;
  
  always_inline void
  vnet_sctp_set_heartbeat_info (sctp_hb_info_param_t * h, u64 hb_info,
  			      u16 hb_info_length)
  {
    h->hb_info = clib_host_to_net_u16 (1);
    h->param_hdr.length = clib_host_to_net_u16 (hb_info_length);
    h->hb_info = clib_host_to_net_u64 (hb_info);
  }
  
  /*
   * Heartbeat Request
   *
   * An endpoint should send this chunk to its peer endpoint to probe the
   * reachability of a particular destination transport address defined in
   * the present association.
   * The parameter field contains the Heartbeat Information, which is a
   * variable-length opaque data structure understood only by the sender.
   */
  /*
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |   Type = 4    | Chunk  Flags  |      Heartbeat Length         |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * \                                                               \
   * /            Heartbeat Information TLV (Variable-Length)        /
   * \                                                               \
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef struct
  {
    sctp_header_t sctp_hdr;
    sctp_chunks_common_hdr_t chunk_hdr;
    sctp_hb_info_param_t hb_info;
  
  } sctp_hb_req_chunk_t;
  
  always_inline void
  vnet_sctp_set_hb_request_info (sctp_hb_req_chunk_t * h,
  			       sctp_hb_info_param_t * hb_info)
  {
    vnet_sctp_set_chunk_type (&h->chunk_hdr, HEARTBEAT);
    memcpy (&h->hb_info, hb_info, sizeof (h->hb_info));
  }
  
  /*
   * Heartbeat Acknowledgement
   *
   * An endpoint should send this chunk to its peer endpoint as a response
   * to a HEARTBEAT chunk.
   * A HEARTBEAT ACK is always sent to the source IP address of the IP datagram
   * containing the HEARTBEAT chunk to which this ack is responding.
   */
  /*
   *
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |   Type = 5    | Chunk  Flags  |    Heartbeat Ack Length       |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * \                                                               \
   * /            Heartbeat Information TLV (Variable-Length)        /
   * \                                                               \
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef sctp_hb_req_chunk_t sctp_hb_ack_chunk_t;
  
  always_inline void
  vnet_sctp_set_hb_ack_info (sctp_hb_ack_chunk_t * h,
  			   sctp_hb_info_param_t * hb_info)
  {
    vnet_sctp_set_chunk_type (&h->chunk_hdr, HEARTBEAT_ACK);
    memcpy (&h->hb_info, hb_info, sizeof (h->hb_info));
  }
  
  /*
   * Error cause
   */
  /*
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |           Cause Code          |       Cause Length            |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * /                    Cause-Specific Information                 /
   * \                                                               \
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  */
  typedef struct
  {
  
    sctp_opt_params_hdr_t param_hdr;
    u64 cause_info;
  
  } sctp_err_cause_param_t;
  
  /*
   * Abort Association (ABORT)
   *
   * The ABORT chunk is sent to the peer of an association to close the
   * association.  The ABORT chunk may contain Cause Parameters to inform
   * the receiver about the reason of the abort.  DATA chunks MUST NOT be
   * bundled with ABORT.  Control chunks (except for INIT, INIT ACK, and
   * SHUTDOWN COMPLETE) MAY be bundled with an ABORT, but they MUST be
   * placed before the ABORT in the SCTP packet or they will be ignored by
   * the receiver.
   *
   * If an endpoint receives an ABORT with a format error or no TCB is
   * found, it MUST silently discard it.  Moreover, under any
   * circumstances, an endpoint that receives an ABORT MUST NOT respond to
   * that ABORT by sending an ABORT of its own.
   */
  /*
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |   Type = 6    |Reserved     |T|           Length              |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * \                                                               \
   * /                   zero or more Error Causes                   /
   * \                                                               \
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef struct
  {
    sctp_header_t sctp_hdr;
    sctp_chunks_common_hdr_t chunk_hdr;
    sctp_err_cause_param_t err_causes[];
  
  } sctp_abort_chunk_t;
  
  always_inline void
  vnet_sctp_set_tbit (sctp_abort_chunk_t * a)
  {
    vnet_sctp_set_chunk_type (&a->chunk_hdr, ABORT);
    // a->chunk_hdr.flags = clib_host_to_net_u16 (1);
  }
  
  always_inline void
  vnet_sctp_unset_tbit (sctp_abort_chunk_t * a)
  {
    vnet_sctp_set_chunk_type (&a->chunk_hdr, ABORT);
    // a->chunk_hdr.flags = clib_host_to_net_u16 (0);
  }
  
  /*
   * Shutdown Association (SHUTDOWN)
   *
   * An endpoint in an association MUST use this chunk to initiate a
   * graceful close of the association with its peer.
   */
  /*
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |   Type = 7    | Chunk  Flags  |      Length = 8               |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |                      Cumulative TSN Ack                       |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef struct
  {
    sctp_header_t sctp_hdr;
    sctp_chunks_common_hdr_t chunk_hdr;
    /*
     * This parameter contains the TSN of the last chunk received in
     * sequence before any gaps.
     *
     * Note: Since the SHUTDOWN message does not contain Gap Ack Blocks,
     * it cannot be used to acknowledge TSNs received out of order. In a
     * SACK, lack of Gap Ack Blocks that were previously included
     * indicates that the data receiver reneged on the associated DATA
     * chunks. Since SHUTDOWN does not contain Gap Ack Blocks, the
     * receiver of the SHUTDOWN shouldn't interpret the lack of a Gap Ack
     * Block as a renege.
     */
    u32 cumulative_tsn_ack;
  
  } sctp_shutdown_association_chunk_t;
  
  always_inline void
  vnet_sctp_set_tsn_last_received_chunk (sctp_shutdown_association_chunk_t * s,
  				       u32 tsn_last_chunk)
  {
    vnet_sctp_set_chunk_type (&s->chunk_hdr, SHUTDOWN);
    s->cumulative_tsn_ack = clib_host_to_net_u32 (tsn_last_chunk);
  }
  
  /*
   * Shutdown Acknowledgement (SHUTDOWN ACK)
   *
   * This chunk MUST be used to acknowledge the receipt of the SHUTDOWN
   * chunk at the completion of the shutdown process.
   */
  /*
   * 0                   1                   2                   3
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * |   Type = 8    |Chunk  Flags   |      Length = 4               |
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   */
  typedef struct
  {
    sctp_header_t sctp_hdr;
    sctp_chunks_common_hdr_t chunk_hdr;
  } sctp_shutdown_ack_chunk_t;
  
  always_inline void
  vnet_sctp_fill_shutdown_ack (sctp_shutdown_ack_chunk_t * s)
  {
    vnet_sctp_set_chunk_type (&s->chunk_hdr, SHUTDOWN_ACK);
    vnet_sctp_set_chunk_length (&s->chunk_hdr, 4);
  }
  
  #endif /* included_vnet_sctp_packet_h */
  
  /*
   * fd.io coding-style-patch-verification: ON
   *
   * Local Variables:
   * eval: (c-set-style "gnu")
   * End:
   */
  