/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */ /* * lsquic_conn.h -- Connection interface * * There are two types of connections: full (lsquic_full_conn.h) and mini * (lsquic_mini_conn.h). The function pointers and struct in this header * file provide a unified interface engine.c can use to interact with * either of the connection types. For this to work, struct lsquic_conn * must be the first element of struct full_conn and struct mini_conn. */ #ifndef LSQUIC_CONN_H #define LSQUIC_CONN_H #include #ifndef WIN32 #include #include #else #include #endif struct lsquic_conn; struct lsquic_engine_public; struct lsquic_packet_out; struct lsquic_packet_in; struct sockaddr; struct parse_funcs; struct attq_elem; #if LSQUIC_CONN_STATS struct conn_stats; #endif enum lsquic_conn_flags { LSCONN_TICKED = (1 << 0), LSCONN_HAS_OUTGOING = (1 << 1), LSCONN_HASHED = (1 << 2), LSCONN_MINI = (1 << 3), /* This is a mini connection */ LSCONN_IMMED_CLOSE = (1 << 4), LSCONN_UNUSED_5 = (1 << 5), LSCONN_HANDSHAKE_DONE = (1 << 6), LSCONN_CLOSING = (1 << 7), LSCONN_PEER_GOING_AWAY= (1 << 8), LSCONN_TCID0 = (1 << 9), LSCONN_VER_SET = (1 <<10), /* cn_version is set */ LSCONN_EVANESCENT = (1 <<11), /* evanescent connection */ LSCONN_TICKABLE = (1 <<12), /* Connection is in the Tickable Queue */ LSCONN_COI_ACTIVE = (1 <<13), LSCONN_COI_INACTIVE = (1 <<14), LSCONN_SEND_BLOCKED = (1 <<15), /* Send connection blocked frame */ LSCONN_PROMOTED = (1 <<16), /* Promoted. Only set if LSCONN_MINI is set */ LSCONN_NEVER_TICKABLE = (1 <<17), /* Do not put onto the Tickable Queue */ LSCONN_UNUSED_18 = (1 <<18), LSCONN_ATTQ = (1 <<19), LSCONN_SKIP_ON_PROC = (1 <<20), LSCONN_UNUSED_21 = (1 <<21), LSCONN_SERVER = (1 <<22), LSCONN_IETF = (1 <<23), LSCONN_RETRY_CONN = (1 <<24), /* This is a retry connection */ }; /* A connection may have things to send and be closed at the same time. */ enum tick_st { TICK_SEND = (1 << 0), TICK_CLOSE = (1 << 1), TICK_PROMOTE = (1 << 2), /* Promote mini connection to full connection */ }; #define TICK_QUIET 0 struct network_path { union { unsigned char buf[sizeof(struct sockaddr_in6)]; struct sockaddr sockaddr; } np_local_addr_u; #define np_local_addr np_local_addr_u.buf unsigned char np_peer_addr[sizeof(struct sockaddr_in6)]; void *np_peer_ctx; lsquic_cid_t np_dcid; unsigned short np_pack_size; unsigned char np_path_id; }; #define NP_LOCAL_SA(path_) (&(path_)->np_local_addr_u.sockaddr) #define NP_PEER_SA(path_) ((struct sockaddr *) (path_)->np_peer_addr) #define NP_IS_IPv6(path_) (AF_INET6 == NP_LOCAL_SA(path_)->sa_family) struct ack_state { uint32_t arr[6]; }; struct to_coal { const struct lsquic_packet_out *prev_packet; size_t prev_sz_sum; }; struct conn_iface { enum tick_st (*ci_tick) (struct lsquic_conn *, lsquic_time_t now); void (*ci_packet_in) (struct lsquic_conn *, struct lsquic_packet_in *); /* Note: all packets "checked out" by calling this method should be * returned back to the connection via ci_packet_sent() or * ci_packet_not_sent() calls before the connection is ticked next. * The connection, in turn, should not perform any extra processing * (especially schedule more packets) during any of these method * calls. This is because the checked out packets are not accounted * for by the congestion controller. */ struct lsquic_packet_out * (*ci_next_packet_to_send) (struct lsquic_conn *, const struct to_coal *); void (*ci_packet_sent) (struct lsquic_conn *, struct lsquic_packet_out *); void (*ci_packet_not_sent) (struct lsquic_conn *, struct lsquic_packet_out *); void (*ci_packet_too_large) (struct lsquic_conn *, struct lsquic_packet_out *); void (*ci_hsk_done) (struct lsquic_conn *, enum lsquic_hsk_status); void (*ci_destroy) (struct lsquic_conn *); int (*ci_is_tickable) (struct lsquic_conn *); lsquic_time_t (*ci_next_tick_time) (struct lsquic_conn *, unsigned *why); int (*ci_can_write_ack) (struct lsquic_conn *); /* No return status: best effort */ void (*ci_write_ack) (struct lsquic_conn *, struct lsquic_packet_out *); #if LSQUIC_CONN_STATS const struct conn_stats * (*ci_get_stats) (struct lsquic_conn *); #endif void (*ci_client_call_on_new) (struct lsquic_conn *); enum LSQUIC_CONN_STATUS (*ci_status) (struct lsquic_conn *, char *errbuf, size_t bufsz); unsigned (*ci_n_avail_streams) (const struct lsquic_conn *); unsigned (*ci_n_pending_streams) (const struct lsquic_conn *); unsigned (*ci_cancel_pending_streams) (struct lsquic_conn *, unsigned n); void (*ci_going_away) (struct lsquic_conn *); int (*ci_is_push_enabled) (struct lsquic_conn *); /* Optional: only used by gQUIC frames reader */ /* If stream is already closed, NULL is returned */ struct lsquic_stream * (*ci_get_stream_by_id) (struct lsquic_conn *, lsquic_stream_id_t stream_id); struct lsquic_engine * (*ci_get_engine) (struct lsquic_conn *); struct lsquic_conn_ctx * (*ci_get_ctx) (const struct lsquic_conn *); void (*ci_set_ctx) (struct lsquic_conn *, struct lsquic_conn_ctx *); void (*ci_make_stream) (struct lsquic_conn *); void (*ci_abort) (struct lsquic_conn *); void (*ci_retire_cid) (struct lsquic_conn *); void (*ci_close) (struct lsquic_conn *); void (*ci_stateless_reset) (struct lsquic_conn *); int (*ci_crypto_keysize) (const struct lsquic_conn *); int (*ci_crypto_alg_keysize) (const struct lsquic_conn *); enum lsquic_crypto_ver (*ci_crypto_ver) (const struct lsquic_conn *); const char * (*ci_crypto_cipher) (const struct lsquic_conn *); int (*ci_push_stream) (struct lsquic_conn *, void *hset, struct lsquic_stream *, const struct lsquic_http_headers *headers); /* Use this to abort the connection when unlikely errors occur */ void (*ci_internal_error) (struct lsquic_conn *, const char *format, ...) #if __GNUC__ __attribute__((format(printf, 2, 3))) #endif ; /* Abort connection with error */ void (*ci_abort_error) (struct lsquic_conn *, int is_app, unsigned error_code, const char *format, ...) #if __GNUC__ __attribute__((format(printf, 4, 5))) #endif ; void (*ci_tls_alert) (struct lsquic_conn *, uint8_t); /* Returns 0 if connection is to be deleted immediately */ lsquic_time_t (*ci_drain_time) (const struct lsquic_conn *); /* Returns true if it's time to report the connection's CIDs' liveness */ int (*ci_report_live) (struct lsquic_conn *, lsquic_time_t now); /* If `local_sa' is NULL, return default path */ struct network_path * (*ci_get_path) (struct lsquic_conn *, const struct sockaddr *local_sa); unsigned char (*ci_record_addrs) (struct lsquic_conn *, void *peer_ctx, const struct sockaddr *local_sa, const struct sockaddr *peer_sa); const lsquic_cid_t * (*ci_get_log_cid) (const struct lsquic_conn *); /* Optional method. Only used by the IETF client code. */ void (*ci_drop_crypto_streams) (struct lsquic_conn *); /* Optional method. Only used by IETF connections */ void (*ci_count_garbage) (struct lsquic_conn *, size_t); /* Optional method. Must be implemented if connection sends MTU probes */ void (*ci_mtu_probe_acked) (struct lsquic_conn *, const struct lsquic_packet_out *); /* Optional method. It is called when RTO occurs. */ void (*ci_retx_timeout) (struct lsquic_conn *); void (*ci_ack_snapshot) (struct lsquic_conn *, struct ack_state *); void (*ci_ack_rollback) (struct lsquic_conn *, struct ack_state *); /* Optional method. */ int (*ci_want_datagram_write) (struct lsquic_conn *, int); /* Optional method */ int (*ci_set_min_datagram_size) (struct lsquic_conn *, size_t); /* Optional method */ size_t (*ci_get_min_datagram_size) (struct lsquic_conn *); }; #define LSCONN_CCE_BITS 3 #define LSCONN_MAX_CCES (1 << LSCONN_CCE_BITS) struct conn_cid_elem { struct lsquic_hash_elem cce_hash_el; /* Must be first element */ lsquic_cid_t cce_cid; union { unsigned seqno; unsigned short port; } cce_u; #define cce_seqno cce_u.seqno #define cce_port cce_u.port enum conn_cce_flags { CCE_USED = 1 << 0, /* Connection ID has been used */ CCE_SEQNO = 1 << 1, /* cce_seqno is set (CIDs in Initial * packets have no sequence number). */ CCE_REG = 1 << 2, /* CID has been registered */ CCE_PORT = 1 << 3, /* It's not a CID element at all: * cce_port is the hash value. */ } cce_flags; }; struct lsquic_conn { void *cn_enc_session; const struct enc_session_funcs_common *cn_esf_c; union { const struct enc_session_funcs_gquic *g; const struct enc_session_funcs_iquic *i; } cn_esf; #define cn_cid cn_cces[0].cce_cid STAILQ_ENTRY(lsquic_conn) cn_next_closed_conn; /* This and cn_next_closed_conn could be made into a union, as new full * connections are never closed. */ STAILQ_ENTRY(lsquic_conn) cn_next_new_full; TAILQ_ENTRY(lsquic_conn) cn_next_ticked; TAILQ_ENTRY(lsquic_conn) cn_next_out; TAILQ_ENTRY(lsquic_conn) cn_next_pr; const struct conn_iface *cn_if; const struct parse_funcs *cn_pf; struct attq_elem *cn_attq_elem; lsquic_time_t cn_last_sent; lsquic_time_t cn_last_ticked; struct conn_cid_elem *cn_cces; /* At least one is available */ enum lsquic_conn_flags cn_flags; enum lsquic_version cn_version:8; unsigned char cn_cces_mask; /* Those that are set */ unsigned char cn_n_cces; /* Number of CCEs in cn_cces */ unsigned char cn_cur_cce_idx; #if LSQUIC_TEST struct conn_cid_elem cn_cces_buf[8]; #define LSCONN_INITIALIZER_CID(lsconn_, cid_) { \ .cn_cces = (lsconn_).cn_cces_buf, \ .cn_cces_buf[0].cce_seqno = 0, \ .cn_cces_buf[0].cce_flags = CCE_SEQNO, \ .cn_cces_buf[0].cce_cid = (cid_), \ .cn_n_cces = 8, .cn_cces_mask = 1, } #define LSCONN_INITIALIZER_CIDLEN(lsconn_, len_) { \ .cn_cces = (lsconn_).cn_cces_buf, \ .cn_cces_buf[0].cce_seqno = 0, \ .cn_cces_buf[0].cce_flags = CCE_SEQNO, \ .cn_cces_buf[0].cce_cid = { .len = len_ }, \ .cn_n_cces = 8, .cn_cces_mask = 1, } #define LSCONN_INITIALIZE(lsconn_) do { \ (lsconn_)->cn_cces = (lsconn_)->cn_cces_buf; \ (lsconn_)->cn_n_cces = 8; (lsconn_)->cn_cces_mask = 1; } while (0) #endif }; #define END_OF_CCES(conn) ((conn)->cn_cces + (conn)->cn_n_cces) #define CN_SCID(conn) (&(conn)->cn_cces[(conn)->cn_cur_cce_idx].cce_cid) unsigned char lsquic_conn_record_sockaddr (lsquic_conn_t *lconn, void *peer_ctx, const struct sockaddr *local_sa, const struct sockaddr *peer_sa); int lsquic_conn_decrypt_packet (lsquic_conn_t *lconn, struct lsquic_engine_public *, struct lsquic_packet_in *); int lsquic_conn_copy_and_release_pi_data (const lsquic_conn_t *conn, struct lsquic_engine_public *, struct lsquic_packet_in *); void lsquic_generate_cid (lsquic_cid_t *cid, size_t len); void lsquic_generate_cid_gquic (lsquic_cid_t *cid); void lsquic_conn_retire_cid (lsquic_conn_t *lconn); #define lsquic_conn_adv_time(c) ((c)->cn_attq_elem->ae_adv_time) #if LSQUIC_CONN_STATS struct conn_stats { /* All counters are of the same type, unsigned long, because we cast the * struct to an array to update the aggregate. */ unsigned long n_ticks; /* How many time connection was ticked */ struct { unsigned long stream_data_sz; /* Sum of all STREAM frames payload */ unsigned long stream_frames; /* Number of STREAM frames */ unsigned long packets, /* Incoming packets */ undec_packets, /* Undecryptable packets */ dup_packets, /* Duplicate packets */ err_packets; /* Error packets(?) */ unsigned long n_acks, n_acks_proc, n_acks_merged; unsigned long bytes; /* Overall bytes in */ unsigned long headers_uncomp; /* Sum of uncompressed header bytes */ unsigned long headers_comp; /* Sum of compressed header bytes */ } in; struct { unsigned long stream_data_sz; unsigned long stream_frames; unsigned long acks; unsigned long packets; /* Number of sent packets */ unsigned long acked_via_loss; /* Number of packets acked via loss record */ unsigned long retx_packets; /* Number of retransmitted packets */ unsigned long bytes; /* Overall bytes out */ unsigned long headers_uncomp; /* Sum of uncompressed header bytes */ unsigned long headers_comp; /* Sum of compressed header bytes */ } out; }; #endif #endif