litespeed-quic/src/liblsquic/lsquic_conn.h

445 lines
15 KiB
C
Raw Normal View History

2022-05-06 16:49:46 +00:00
/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */
2017-09-22 21:00:03 +00:00
/*
* 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.
2017-09-22 21:00:03 +00:00
*/
#ifndef LSQUIC_CONN_H
#define LSQUIC_CONN_H
#include <sys/queue.h>
#ifndef WIN32
2017-09-22 21:00:03 +00:00
#include <sys/socket.h>
#include <netinet/in.h>
#else
#include <ws2ipdef.h>
#endif
2017-09-22 21:00:03 +00:00
#ifndef LSQUIC_TEST
#define LSQUIC_TEST 0
#endif
2017-09-22 21:00:03 +00:00
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
2017-09-22 21:00:03 +00:00
enum lsquic_conn_flags {
LSCONN_TICKED = (1 << 0),
2017-09-22 21:00:03 +00:00
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),
2017-09-22 21:00:03 +00:00
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 */
[API Change, OPTIMIZATION] Only process conns that need to be processed The API is simplified: do not expose the user code to several queues. A "connection queue" is now an internal concept. The user processes connections using the single function lsquic_engine_process_conns(). When this function is called, only those connections are processed that need to be processed. A connection needs to be processed when: 1. New incoming packets have been fed to the connection. 2. User wants to read from a stream that is readable. 3. User wants to write to a stream that is writeable. 4. There are buffered packets that can be sent out. (This means that the user wrote to a stream outside of the lsquic library callback.) 5. A control frame (such as BLOCKED) needs to be sent out. 6. A stream needs to be serviced or delayed stream needs to be created. 7. An alarm rings. 8. Pacer timer expires. To achieve this, the library places the connections into two priority queues (min heaps): 1. Tickable Queue; and 2. Advisory Tick Time queue (ATTQ). Each time lsquic_engine_process_conns() is called, the Tickable Queue is emptied. After the connections have been ticked, they are queried again: if a connection is not being closed, it is placed either in the Tickable Queue if it is ready to be ticked again or it is placed in the Advisory Tick Time Queue. It is assumed that a connection always has at least one timer set (the idle alarm). The connections in the Tickable Queue are arranged in the least recently ticked order. This lets connections that have been quiet longer to get their packets scheduled first. This change means that the library no longer needs to be ticked periodically. The user code can query the library when is the next tick event and schedule it exactly. When connections are processed, only the tickable connections are processed, not *all* the connections. When there are no tick events, it means that no timer event is necessary -- only the file descriptor READ event is active. The following are improvements and simplifications that have been triggered: - Queue of connections with incoming packets is gone. - "Pending Read/Write Events" Queue is gone (along with its history and progress checks). This queue has become the Tickable Queue. - The connection hash no longer needs to track the connection insertion order.
2018-04-09 13:39:38 +00:00
LSCONN_TICKABLE = (1 <<12), /* Connection is in the Tickable Queue */
2017-09-22 21:00:03 +00:00
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 */
[API Change, OPTIMIZATION] Only process conns that need to be processed The API is simplified: do not expose the user code to several queues. A "connection queue" is now an internal concept. The user processes connections using the single function lsquic_engine_process_conns(). When this function is called, only those connections are processed that need to be processed. A connection needs to be processed when: 1. New incoming packets have been fed to the connection. 2. User wants to read from a stream that is readable. 3. User wants to write to a stream that is writeable. 4. There are buffered packets that can be sent out. (This means that the user wrote to a stream outside of the lsquic library callback.) 5. A control frame (such as BLOCKED) needs to be sent out. 6. A stream needs to be serviced or delayed stream needs to be created. 7. An alarm rings. 8. Pacer timer expires. To achieve this, the library places the connections into two priority queues (min heaps): 1. Tickable Queue; and 2. Advisory Tick Time queue (ATTQ). Each time lsquic_engine_process_conns() is called, the Tickable Queue is emptied. After the connections have been ticked, they are queried again: if a connection is not being closed, it is placed either in the Tickable Queue if it is ready to be ticked again or it is placed in the Advisory Tick Time Queue. It is assumed that a connection always has at least one timer set (the idle alarm). The connections in the Tickable Queue are arranged in the least recently ticked order. This lets connections that have been quiet longer to get their packets scheduled first. This change means that the library no longer needs to be ticked periodically. The user code can query the library when is the next tick event and schedule it exactly. When connections are processed, only the tickable connections are processed, not *all* the connections. When there are no tick events, it means that no timer event is necessary -- only the file descriptor READ event is active. The following are improvements and simplifications that have been triggered: - Queue of connections with incoming packets is gone. - "Pending Read/Write Events" Queue is gone (along with its history and progress checks). This queue has become the Tickable Queue. - The connection hash no longer needs to track the connection insertion order.
2018-04-09 13:39:38 +00:00
LSCONN_NEVER_TICKABLE = (1 <<17), /* Do not put onto the Tickable Queue */
LSCONN_UNUSED_18 = (1 <<18),
2017-09-22 21:00:03 +00:00
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 */
2017-09-22 21:00:03 +00:00
};
/* 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 */
2017-09-22 21:00:03 +00:00
};
#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;
};
2017-09-22 21:00:03 +00:00
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.
*/
2017-09-22 21:00:03 +00:00
struct lsquic_packet_out *
(*ci_next_packet_to_send) (struct lsquic_conn *, const struct to_coal *);
2017-09-22 21:00:03 +00:00
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 *);
2017-09-22 21:00:03 +00:00
void
(*ci_hsk_done) (struct lsquic_conn *, enum lsquic_hsk_status);
2017-09-22 21:00:03 +00:00
void
(*ci_destroy) (struct lsquic_conn *);
[API Change, OPTIMIZATION] Only process conns that need to be processed The API is simplified: do not expose the user code to several queues. A "connection queue" is now an internal concept. The user processes connections using the single function lsquic_engine_process_conns(). When this function is called, only those connections are processed that need to be processed. A connection needs to be processed when: 1. New incoming packets have been fed to the connection. 2. User wants to read from a stream that is readable. 3. User wants to write to a stream that is writeable. 4. There are buffered packets that can be sent out. (This means that the user wrote to a stream outside of the lsquic library callback.) 5. A control frame (such as BLOCKED) needs to be sent out. 6. A stream needs to be serviced or delayed stream needs to be created. 7. An alarm rings. 8. Pacer timer expires. To achieve this, the library places the connections into two priority queues (min heaps): 1. Tickable Queue; and 2. Advisory Tick Time queue (ATTQ). Each time lsquic_engine_process_conns() is called, the Tickable Queue is emptied. After the connections have been ticked, they are queried again: if a connection is not being closed, it is placed either in the Tickable Queue if it is ready to be ticked again or it is placed in the Advisory Tick Time Queue. It is assumed that a connection always has at least one timer set (the idle alarm). The connections in the Tickable Queue are arranged in the least recently ticked order. This lets connections that have been quiet longer to get their packets scheduled first. This change means that the library no longer needs to be ticked periodically. The user code can query the library when is the next tick event and schedule it exactly. When connections are processed, only the tickable connections are processed, not *all* the connections. When there are no tick events, it means that no timer event is necessary -- only the file descriptor READ event is active. The following are improvements and simplifications that have been triggered: - Queue of connections with incoming packets is gone. - "Pending Read/Write Events" Queue is gone (along with its history and progress checks). This queue has become the Tickable Queue. - The connection hash no longer needs to track the connection insertion order.
2018-04-09 13:39:38 +00:00
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 *);
void
(*ci_log_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 *);
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 *);
/* Optional method */
void
(*ci_early_data_failed) (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;
[API Change, OPTIMIZATION] Only process conns that need to be processed The API is simplified: do not expose the user code to several queues. A "connection queue" is now an internal concept. The user processes connections using the single function lsquic_engine_process_conns(). When this function is called, only those connections are processed that need to be processed. A connection needs to be processed when: 1. New incoming packets have been fed to the connection. 2. User wants to read from a stream that is readable. 3. User wants to write to a stream that is writeable. 4. There are buffered packets that can be sent out. (This means that the user wrote to a stream outside of the lsquic library callback.) 5. A control frame (such as BLOCKED) needs to be sent out. 6. A stream needs to be serviced or delayed stream needs to be created. 7. An alarm rings. 8. Pacer timer expires. To achieve this, the library places the connections into two priority queues (min heaps): 1. Tickable Queue; and 2. Advisory Tick Time queue (ATTQ). Each time lsquic_engine_process_conns() is called, the Tickable Queue is emptied. After the connections have been ticked, they are queried again: if a connection is not being closed, it is placed either in the Tickable Queue if it is ready to be ticked again or it is placed in the Advisory Tick Time Queue. It is assumed that a connection always has at least one timer set (the idle alarm). The connections in the Tickable Queue are arranged in the least recently ticked order. This lets connections that have been quiet longer to get their packets scheduled first. This change means that the library no longer needs to be ticked periodically. The user code can query the library when is the next tick event and schedule it exactly. When connections are processed, only the tickable connections are processed, not *all* the connections. When there are no tick events, it means that no timer event is necessary -- only the file descriptor READ event is active. The following are improvements and simplifications that have been triggered: - Queue of connections with incoming packets is gone. - "Pending Read/Write Events" Queue is gone (along with its history and progress checks). This queue has become the Tickable Queue. - The connection hash no longer needs to track the connection insertion order.
2018-04-09 13:39:38 +00:00
};
2017-09-22 21:00:03 +00:00
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
2017-09-22 21:00:03 +00:00
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;
2017-09-22 21:00:03 +00:00
const struct conn_iface *cn_if;
const struct parse_funcs *cn_pf;
struct attq_elem *cn_attq_elem;
lsquic_time_t cn_last_sent;
[API Change, OPTIMIZATION] Only process conns that need to be processed The API is simplified: do not expose the user code to several queues. A "connection queue" is now an internal concept. The user processes connections using the single function lsquic_engine_process_conns(). When this function is called, only those connections are processed that need to be processed. A connection needs to be processed when: 1. New incoming packets have been fed to the connection. 2. User wants to read from a stream that is readable. 3. User wants to write to a stream that is writeable. 4. There are buffered packets that can be sent out. (This means that the user wrote to a stream outside of the lsquic library callback.) 5. A control frame (such as BLOCKED) needs to be sent out. 6. A stream needs to be serviced or delayed stream needs to be created. 7. An alarm rings. 8. Pacer timer expires. To achieve this, the library places the connections into two priority queues (min heaps): 1. Tickable Queue; and 2. Advisory Tick Time queue (ATTQ). Each time lsquic_engine_process_conns() is called, the Tickable Queue is emptied. After the connections have been ticked, they are queried again: if a connection is not being closed, it is placed either in the Tickable Queue if it is ready to be ticked again or it is placed in the Advisory Tick Time Queue. It is assumed that a connection always has at least one timer set (the idle alarm). The connections in the Tickable Queue are arranged in the least recently ticked order. This lets connections that have been quiet longer to get their packets scheduled first. This change means that the library no longer needs to be ticked periodically. The user code can query the library when is the next tick event and schedule it exactly. When connections are processed, only the tickable connections are processed, not *all* the connections. When there are no tick events, it means that no timer event is necessary -- only the file descriptor READ event is active. The following are improvements and simplifications that have been triggered: - Queue of connections with incoming packets is gone. - "Pending Read/Write Events" Queue is gone (along with its history and progress checks). This queue has become the Tickable Queue. - The connection hash no longer needs to track the connection insertion order.
2018-04-09 13:39:38 +00:00
lsquic_time_t cn_last_ticked;
struct conn_cid_elem *cn_cces; /* At least one is available */
lsquic_conn_ctx_t *cn_conn_ctx;
2017-09-22 21:00:03 +00:00
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
2017-09-22 21:00:03 +00:00
};
#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);
2017-09-22 21:00:03 +00:00
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_generate_scid (void *, struct lsquic_conn *lconn, lsquic_cid_t *scid,
unsigned len);
void
lsquic_conn_retire_cid (lsquic_conn_t *lconn);
2017-09-22 21:00:03 +00:00
#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 lost_packets;
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;
};
void
lsquic_conn_stats_diff (const struct conn_stats *cumulative,
const struct conn_stats *previous,
struct conn_stats *new);
#endif
2017-09-22 21:00:03 +00:00
#endif