/* Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc.  See LICENSE. */
/*
 * lsquic_mini_conn_ietf.h -- Mini connection used by the IETF QUIC
 */

#ifndef LSQUIC_MINI_CONN_IETF_H
#define LSQUIC_MINI_CONN_IETF_H 1

struct lsquic_conn;
struct lsquic_engine_public;
struct lsquic_packet_in;

enum { MCSBIT_WANTREAD, MCSBIT_WANTWRITE, };

struct mini_crypto_stream
{
    unsigned        mcs_read_off;
    unsigned        mcs_write_off;
    enum {
        MCS_WANTREAD    = 1 << MCSBIT_WANTREAD,
        MCS_WANTWRITE   = 1 << MCSBIT_WANTWRITE,
        MCS_CREATED     = 1 << 2,
    }               mcs_flags:8;
    enum enc_level  mcs_enc_level:8;
};

typedef uint64_t packno_set_t;
#define MAX_PACKETS ((sizeof(packno_set_t) * 8) - 1)

/* We do not handle packets in the App packet number space in the mini
 * connection.  They are all buffered to be handled later when the
 * connection is promoted.  This means we do not have to have data
 * structures to track the App PNS.
 */
#define IMICO_N_PNS (N_PNS - 1)

struct ietf_mini_conn
{
    struct lsquic_conn              imc_conn;
    struct conn_cid_elem            imc_cces[3];
    struct lsquic_engine_public    *imc_enpub;
    lsquic_time_t                   imc_created;
    enum {
        IMC_ENC_SESS_INITED     = 1 << 0,
        IMC_QUEUED_ACK_INIT     = 1 << 1,
        IMC_QUEUED_ACK_HSK      = IMC_QUEUED_ACK_INIT << PNS_HSK,
        IMC_UNUSED3             = 1 << 3,
        IMC_ERROR               = 1 << 4,
        IMC_HSK_OK              = 1 << 5,
        IMC_HSK_FAILED          = 1 << 6,
        IMC_HAVE_TP             = 1 << 7,
        IMC_RETRY_MODE          = 1 << 8,
        IMC_RETRY_DONE          = 1 << 9,
        IMC_IGNORE_INIT         = 1 << 10,
#define IMCBIT_PNS_BIT_SHIFT 11
        IMC_MAX_PNS_BIT_0       = 1 << 11,
        IMC_MAX_PNS_BIT_1       = 1 << 12,
        IMC_TLS_ALERT           = 1 << 13,
        IMC_ABORT_ERROR         = 1 << 14,
        IMC_ABORT_ISAPP         = 1 << 15,
        IMC_BAD_TRANS_PARAMS    = 1 << 16,
        IMC_ADDR_VALIDATED      = 1 << 17,
        IMC_HSK_PACKET_SENT     = 1 << 18,
        IMC_CLOSE_RECVD         = 1 << 19,
        IMC_PARSE_FAILED        = 1 << 20,
        IMC_PATH_CHANGED        = 1 << 21,
        IMC_HSK_DONE_SENT       = 1 << 22,
        IMC_TRECHIST            = 1 << 23,
    }                               imc_flags;
    struct mini_crypto_stream       imc_streams[N_ENC_LEVS];
    void                           *imc_stream_ps[N_ENC_LEVS];
    struct {
        struct stream_frame    *frame;   /* Latest frame - on stack - be careful. */
        enum enc_level          enc_level;
    }                               imc_last_in;
    TAILQ_HEAD(, lsquic_packet_in)  imc_app_packets;
    TAILQ_HEAD(, lsquic_packet_out) imc_packets_out;
    TAILQ_HEAD(, stream_frame)      imc_crypto_frames;
    packno_set_t                    imc_sent_packnos;
    union {
        packno_set_t                    bitmasks[IMICO_N_PNS];
        struct {
            struct trechist_elem       *hist_elems;
            trechist_mask_t             hist_masks[IMICO_N_PNS];
        }                           trechist;
    }                               imc_recvd_packnos;
    packno_set_t                    imc_acked_packnos[IMICO_N_PNS];
    lsquic_time_t                   imc_largest_recvd[IMICO_N_PNS];
    struct lsquic_rtt_stats         imc_rtt_stats;
    unsigned                        imc_error_code;
    unsigned                        imc_bytes_in;
    unsigned                        imc_bytes_out;
    unsigned short                  imc_crypto_frames_sz;
    /* We need to read in the length of ClientHello to check when we have fed
     * it to the crypto layer.
     */
    unsigned short                  imc_ch_len;
    unsigned char                   imc_next_packno;
    unsigned char                   imc_hsk_count;
    /* We don't send more than eight in the first flight, and so it's OK to
     * use uint8_t.  This value is also used as a boolean: when ECN black
     * hole is detected, it is set to zero to indicate that black hole
     * detection is no longer active.
     */
    uint8_t                         imc_ecn_packnos;
    uint8_t                         imc_ack_exp;
    uint8_t                         imc_ecn_counts_in[IMICO_N_PNS][4];
    uint8_t                         imc_incoming_ecn;
    uint8_t                         imc_tls_alert;
#define IMICO_MAX_DELAYED_PACKETS_UNVALIDATED 1u
#define IMICO_MAX_DELAYED_PACKETS_VALIDATED 2u
    unsigned char                   imc_delayed_packets_count;
#define IMICO_MAX_STASHED_FRAMES 10u
    unsigned char                   imc_n_crypto_frames;
    struct network_path             imc_path;
};

/* [draft-ietf-quic-transport-24] Section 7.4
 *
 " Implementations MUST support buffering at least 4096 bytes of data
 " received in CRYPTO frames out of order.  Endpoints MAY choose to
 " allow more data to be buffered during the handshake.  A larger limit
 " during the handshake could allow for larger keys or credentials to be
 " exchanged.  An endpoint's buffer size does not need to remain
 " constant during the life of the connection.
 */
#define IMICO_MAX_BUFFERED_CRYPTO (6u * 1024u)

struct lsquic_conn *
lsquic_mini_conn_ietf_new (struct lsquic_engine_public *,
               const struct lsquic_packet_in *,
               enum lsquic_version, int is_ipv4, const struct lsquic_cid *,
               size_t udp_payload_size);

int
lsquic_mini_conn_ietf_ecn_ok (const struct ietf_mini_conn *);

struct ietf_mini_rechist
{
    const struct ietf_mini_conn *conn;
    enum packnum_space           pns;
    union {
        struct {
            packno_set_t                 cur_set;
            struct lsquic_packno_range   range;   /* We return a pointer to this */
            int                          cur_idx;
        }                       bitmask;
        struct trechist_iter    trechist_iter;
    } u;
};

void
lsquic_imico_rechist_init (struct ietf_mini_rechist *rechist,
                const struct ietf_mini_conn *conn, enum packnum_space pns);

const struct lsquic_packno_range *
lsquic_imico_rechist_first (void *rechist_ctx);

const struct lsquic_packno_range *
lsquic_imico_rechist_next (void *rechist_ctx);

#endif