src/transport/xqc_conn.c (5,241 lines of code) (raw):
/**
* @copyright Copyright (c) 2022, Alibaba Group Holding Limited
*/
#include <xquic/xquic.h>
#include <errno.h>
#include "src/http3/xqc_h3_stream.h"
#include "src/http3/xqc_h3_conn.h"
#include "src/common/xqc_algorithm.h"
#include "src/common/xqc_common.h"
#include "src/common/xqc_malloc.h"
#include "src/common/xqc_str_hash.h"
#include "src/common/xqc_hash.h"
#include "src/common/xqc_priority_q.h"
#include "src/common/xqc_memory_pool.h"
#include "src/common/xqc_id_hash.h"
#include "src/transport/xqc_defs.h"
#include "src/transport/xqc_conn.h"
#include "src/transport/xqc_send_ctl.h"
#include "src/transport/xqc_recv_timestamps_info.h"
#include "src/transport/xqc_send_queue.h"
#include "src/transport/xqc_engine.h"
#include "src/transport/xqc_cid.h"
#include "src/transport/xqc_stream.h"
#include "src/transport/xqc_frame_parser.h"
#include "src/transport/xqc_packet_parser.h"
#include "src/transport/xqc_utils.h"
#include "src/transport/xqc_multipath.h"
#include "src/transport/xqc_reinjection.h"
#include "src/transport/xqc_datagram.h"
#include "src/transport/xqc_packet.h"
#include "src/transport/xqc_fec.h"
#include "src/transport/xqc_fec_scheme.h"
#include "src/tls/xqc_tls.h"
#include <inttypes.h>
xqc_conn_settings_t internal_default_conn_settings = {
.pacing_on = 0,
.ping_on = 0,
.so_sndbuf = 0,
.sndq_packets_used_max = 0,
.linger = {.linger_on = 0, .linger_timeout = 0},
.proto_version = XQC_VERSION_V1,
.init_idle_time_out = XQC_CONN_INITIAL_IDLE_TIMEOUT,
.idle_time_out = XQC_CONN_DEFAULT_IDLE_TIMEOUT,
.enable_multipath = 0,
.multipath_version = XQC_MULTIPATH_10,
.spurious_loss_detect_on = 0,
.anti_amplification_limit = XQC_DEFAULT_ANTI_AMPLIFICATION_LIMIT,
.keyupdate_pkt_threshold = 0,
.max_pkt_out_size = XQC_PACKET_OUT_SIZE,
.probing_pkt_out_size = XQC_MAX_PACKET_OUT_SIZE,
.max_datagram_frame_size = 0,
.mp_enable_reinjection = 0,
.mp_ack_on_any_path = 0,
.mp_ping_on = 0,
.max_ack_delay = XQC_DEFAULT_MAX_ACK_DELAY,
.ack_frequency = 2,
.loss_detection_pkt_thresh = XQC_kPacketThreshold,
.pto_backoff_factor = 2.0,
.datagram_redundancy = 0,
.datagram_force_retrans_on = 0,
.datagram_redundant_probe = 0,
.reinj_flexible_deadline_srtt_factor = 1.1,
.reinj_hard_deadline = 500000, /* 500ms */
.reinj_deadline_lower_bound = 20000, /* 20ms */
.standby_path_probe_timeout = 0,
.fec_conn_queue_rpr_timeout = 0,
.enable_pmtud = 0,
.pmtud_probing_interval = 500000,
.marking_reinjection = 0,
.recv_rate_bytes_per_sec = 0,
.enable_stream_rate_limit = 0,
.close_dgram_redundancy= XQC_RED_NOT_USE,
.scheduler_params = {
.bw_Bps_thr = 375000,
.loss_percent_thr_high = 30,
.loss_percent_thr_low = 10,
.pto_cnt_thr = 2,
.rtt_us_thr_high = 2000000,
.rtt_us_thr_low = 500000
},
.is_interop_mode = 0,
#ifdef XQC_PROTECT_POOL_MEM
.protect_pool_mem = 0,
#endif
.fec_level = XQC_FEC_CONN_LEVEL, /** 0: connection level; 1: stream level */
.enable_encode_fec = 0,
.enable_decode_fec = 0,
.fec_params = {
.fec_code_rate = 0,
.fec_ele_bit_size = XQC_FEC_ELE_BIT_SIZE_DEFAULT,
.fec_protected_frames = XQC_FRAME_BIT_DATAGRAM | XQC_FRAME_BIT_STREAM,
.fec_max_window_size = XQC_SYMBOL_CACHE_LEN,
.fec_mp_mode = XQC_FEC_MP_DEFAULT,
.fec_max_symbol_num_per_block = 10,
.fec_encoder_schemes_num = 0,
.fec_decoder_schemes_num = 0,
.fec_encoder_scheme = 0,
.fec_decoder_scheme = 0,
.fec_blk_log_mod = 1000,
.fec_packet_mask_mode = 0,
.fec_log_on = 0,
},
.disable_send_mmsg = 0,
.init_max_path_id = XQC_DEFAULT_INIT_MAX_PATH_ID,
.control_pto_value = 0,
.max_udp_payload_size = XQC_CONN_MAX_UDP_PAYLOAD_SIZE,
.extended_ack_features = 0,
.max_receive_timestamps_per_ack = 0,
.receive_timestamps_exponent = 0,
.disable_pn_skipping = 0
};
static void
xqc_conn_dgram_probe_timeout(xqc_gp_timer_id_t gp_timer_id,
xqc_usec_t now, void *user_data)
{
xqc_connection_t *conn = user_data;
xqc_int_t ret = XQC_OK;
size_t probe_size;
if (conn->last_dgram && conn->last_dgram->data_len != 0) {
probe_size = conn->last_dgram->data_len;
ret = xqc_datagram_send(conn, conn->last_dgram->data, probe_size, NULL, XQC_DATA_QOS_PROBING);
xqc_log(conn->log, XQC_LOG_DEBUG, "|timer_based_dgram_probe|ret:%d|dgram_sz:%z|", ret, probe_size);
}
}
void
xqc_conn_set_default_sched_params(xqc_conn_settings_t *src_settings, xqc_conn_settings_t *settings)
{
if (settings->scheduler_params.bw_Bps_thr == 0) {
settings->scheduler_params.bw_Bps_thr = src_settings->scheduler_params.bw_Bps_thr;
}
if (settings->scheduler_params.loss_percent_thr_high == 0) {
settings->scheduler_params.loss_percent_thr_high = src_settings->scheduler_params.loss_percent_thr_high;
}
if (settings->scheduler_params.loss_percent_thr_low == 0) {
settings->scheduler_params.loss_percent_thr_low = src_settings->scheduler_params.loss_percent_thr_low;
}
if (settings->scheduler_params.pto_cnt_thr == 0) {
settings->scheduler_params.pto_cnt_thr = src_settings->scheduler_params.pto_cnt_thr;
}
if (settings->scheduler_params.rtt_us_thr_high == 0) {
settings->scheduler_params.rtt_us_thr_high = src_settings->scheduler_params.rtt_us_thr_high;
}
if (settings->scheduler_params.rtt_us_thr_low == 0) {
settings->scheduler_params.rtt_us_thr_low = src_settings->scheduler_params.rtt_us_thr_low;
}
}
void
xqc_server_set_conn_settings(xqc_engine_t *engine, const xqc_conn_settings_t *settings)
{
engine->default_conn_settings.cong_ctrl_callback = settings->cong_ctrl_callback;
engine->default_conn_settings.cc_params = settings->cc_params;
engine->default_conn_settings.pacing_on = settings->pacing_on;
engine->default_conn_settings.ping_on = settings->ping_on;
engine->default_conn_settings.so_sndbuf = settings->so_sndbuf;
engine->default_conn_settings.sndq_packets_used_max = settings->sndq_packets_used_max;
engine->default_conn_settings.linger = settings->linger;
engine->default_conn_settings.spurious_loss_detect_on = settings->spurious_loss_detect_on;
engine->default_conn_settings.datagram_force_retrans_on = settings->datagram_force_retrans_on;
engine->default_conn_settings.enable_pmtud = settings->enable_pmtud;
engine->default_conn_settings.marking_reinjection = settings->marking_reinjection;
engine->default_conn_settings.mp_ack_on_any_path = settings->mp_ack_on_any_path;
engine->default_conn_settings.mp_ping_on = settings->mp_ping_on;
engine->default_conn_settings.recv_rate_bytes_per_sec = settings->recv_rate_bytes_per_sec;
engine->default_conn_settings.enable_stream_rate_limit = settings->enable_stream_rate_limit;
engine->default_conn_settings.init_recv_window = settings->init_recv_window;
engine->default_conn_settings.initial_rtt = settings->initial_rtt;
engine->default_conn_settings.initial_pto_duration = settings->initial_pto_duration;
engine->default_conn_settings.disable_send_mmsg = settings->disable_send_mmsg;
#ifdef XQC_PROTECT_POOL_MEM
engine->default_conn_settings.protect_pool_mem = settings->protect_pool_mem;
#endif
engine->default_conn_settings.adaptive_ack_frequency = settings->adaptive_ack_frequency;
engine->default_conn_settings.scheduler_params = settings->scheduler_params;
xqc_conn_set_default_sched_params(&internal_default_conn_settings, &engine->default_conn_settings);
if (settings->max_udp_payload_size != 0) {
engine->default_conn_settings.max_udp_payload_size = settings->max_udp_payload_size;
}
if (engine->default_conn_settings.init_recv_window) {
engine->default_conn_settings.init_recv_window = xqc_max(engine->default_conn_settings.init_recv_window, XQC_QUIC_MAX_MSS);
}
if (settings->pmtud_probing_interval) {
engine->default_conn_settings.pmtud_probing_interval = settings->pmtud_probing_interval;
}
if (settings->max_ack_delay) {
engine->default_conn_settings.max_ack_delay = xqc_min(settings->max_ack_delay, XQC_DEFAULT_MAX_ACK_DELAY);
}
if (settings->datagram_redundant_probe) {
engine->default_conn_settings.datagram_redundant_probe = xqc_max(settings->datagram_redundant_probe,
XQC_MIN_DATAGRAM_REDUNDANT_PROBE_INTERVAL);
}
if (settings->datagram_redundancy <= XQC_MAX_DATAGRAM_REDUNDANCY) {
engine->default_conn_settings.datagram_redundancy = settings->datagram_redundancy;
}
if (settings->init_idle_time_out > 0) {
engine->default_conn_settings.init_idle_time_out = settings->init_idle_time_out;
}
if (settings->fec_conn_queue_rpr_timeout > 0) {
engine->default_conn_settings.fec_conn_queue_rpr_timeout = settings->fec_conn_queue_rpr_timeout;
}
if (settings->idle_time_out > 0) {
engine->default_conn_settings.idle_time_out = settings->idle_time_out;
}
if (settings->anti_amplification_limit > 0) {
engine->default_conn_settings.anti_amplification_limit = settings->anti_amplification_limit;
}
if (xqc_check_proto_version_valid(settings->proto_version)) {
engine->default_conn_settings.proto_version = settings->proto_version;
}
engine->default_conn_settings.keyupdate_pkt_threshold = settings->keyupdate_pkt_threshold;
engine->default_conn_settings.max_datagram_frame_size = settings->max_datagram_frame_size;
if (settings->max_pkt_out_size != 0) {
engine->default_conn_settings.max_pkt_out_size = settings->max_pkt_out_size;
}
if (engine->default_conn_settings.max_pkt_out_size > XQC_MAX_PACKET_OUT_SIZE) {
engine->default_conn_settings.max_pkt_out_size = XQC_MAX_PACKET_OUT_SIZE;
}
engine->default_conn_settings.enable_multipath = settings->enable_multipath;
engine->default_conn_settings.is_interop_mode = settings->is_interop_mode;
if (xqc_conn_is_current_mp_version_supported(settings->multipath_version) == XQC_OK) {
engine->default_conn_settings.multipath_version = settings->multipath_version;
} else {
engine->default_conn_settings.multipath_version = XQC_MULTIPATH_10;
}
if (settings->init_max_path_id == 0) {
engine->default_conn_settings.init_max_path_id = XQC_DEFAULT_INIT_MAX_PATH_ID;
} else {
engine->default_conn_settings.init_max_path_id = settings->init_max_path_id;
}
engine->default_conn_settings.close_dgram_redundancy = settings->close_dgram_redundancy;
#ifdef XQC_ENABLE_FEC
engine->default_conn_settings.enable_encode_fec = settings->enable_encode_fec;
if (engine->default_conn_settings.enable_encode_fec) {
xqc_set_fec_schemes(settings->fec_params.fec_encoder_schemes, settings->fec_params.fec_encoder_schemes_num,
engine->default_conn_settings.fec_params.fec_encoder_schemes, &engine->default_conn_settings.fec_params.fec_encoder_schemes_num);
/* if no fec scheme was negotiated succesfully, set enable_encode_fec to 0 */
engine->default_conn_settings.enable_encode_fec = engine->default_conn_settings.fec_params.fec_encoder_schemes_num == 0 ? 0 : settings->enable_encode_fec;
if (settings->fec_params.fec_code_rate) {
engine->default_conn_settings.fec_params.fec_code_rate = settings->fec_params.fec_code_rate;
}
if (settings->fec_params.fec_max_symbol_num_per_block) {
engine->default_conn_settings.fec_params.fec_max_symbol_num_per_block = settings->fec_params.fec_max_symbol_num_per_block;
}
if (settings->fec_params.fec_mp_mode) {
engine->default_conn_settings.fec_params.fec_mp_mode = settings->fec_params.fec_mp_mode;
}
if (settings->fec_level) {
engine->default_conn_settings.fec_level = settings->fec_level;
}
}
engine->default_conn_settings.enable_decode_fec = settings->enable_decode_fec;
if (engine->default_conn_settings.enable_decode_fec) {
xqc_set_fec_schemes(settings->fec_params.fec_decoder_schemes, settings->fec_params.fec_decoder_schemes_num,
engine->default_conn_settings.fec_params.fec_decoder_schemes, &engine->default_conn_settings.fec_params.fec_decoder_schemes_num);
/* if no fec scheme was negotiated succesfully, set enable_decode_fec to 0 */
engine->default_conn_settings.enable_decode_fec = engine->default_conn_settings.fec_params.fec_decoder_schemes_num == 0 ? 0 : settings->enable_decode_fec;
if (settings->fec_params.fec_max_window_size) {
engine->default_conn_settings.fec_params.fec_max_window_size = xqc_min(settings->fec_params.fec_max_window_size, XQC_SYMBOL_CACHE_LEN);
}
if (settings->fec_params.fec_blk_log_mod) {
engine->default_conn_settings.fec_params.fec_blk_log_mod = settings->fec_params.fec_blk_log_mod;
}
if (settings->fec_params.fec_log_on) {
engine->default_conn_settings.fec_params.fec_log_on = settings->fec_params.fec_log_on;
}
if (settings->fec_params.fec_packet_mask_mode) {
engine->default_conn_settings.fec_params.fec_packet_mask_mode = settings->fec_params.fec_packet_mask_mode;
}
}
#endif
engine->default_conn_settings.scheduler_callback = settings->scheduler_callback;
engine->default_conn_settings.reinj_ctl_callback = settings->reinj_ctl_callback;
engine->default_conn_settings.mp_enable_reinjection = settings->mp_enable_reinjection;
if ((settings->extended_ack_features & XQC_ACK_EXT_FEATURE_BIT_RECV_TS)
&& !settings->enable_multipath)
{
/*
* Bit 1 indicates whether Receive Timestamps are enabled.
* Currently, we only use receive_timestamps features, so set extended_ack_features = XQC_ACK_EXT_FEATURE_BIT_RECV_TS.
*
* Besides, multipath-quic and ack_ext are incompatible right now, disable ack_ext when enable multipath.
* TODO: coordinate them in the future.
*/
engine->default_conn_settings.extended_ack_features = XQC_ACK_EXT_FEATURE_BIT_RECV_TS;
engine->default_conn_settings.max_receive_timestamps_per_ack = settings->max_receive_timestamps_per_ack;
engine->default_conn_settings.receive_timestamps_exponent = settings->receive_timestamps_exponent;
}
if (settings->ack_frequency > 0) {
engine->default_conn_settings.ack_frequency = settings->ack_frequency;
}
if (settings->pto_backoff_factor > 0) {
engine->default_conn_settings.pto_backoff_factor = settings->pto_backoff_factor;
}
if (settings->loss_detection_pkt_thresh > 0) {
engine->default_conn_settings.loss_detection_pkt_thresh = settings->loss_detection_pkt_thresh;
}
if (settings->reinj_flexible_deadline_srtt_factor > 0) {
engine->default_conn_settings.reinj_flexible_deadline_srtt_factor = settings->reinj_flexible_deadline_srtt_factor;
}
if (settings->reinj_hard_deadline > 0) {
engine->default_conn_settings.reinj_hard_deadline = settings->reinj_hard_deadline;
}
if (settings->reinj_deadline_lower_bound > 0) {
engine->default_conn_settings.reinj_deadline_lower_bound = settings->reinj_deadline_lower_bound;
}
if (settings->standby_path_probe_timeout > 0) {
/* no less than 500ms */
engine->default_conn_settings.standby_path_probe_timeout = xqc_max(settings->standby_path_probe_timeout, XQC_MIN_STANDBY_RPOBE_TIMEOUT);
}
if (settings->keyupdate_pkt_threshold != UINT64_MAX) {
engine->default_conn_settings.keyupdate_pkt_threshold = settings->keyupdate_pkt_threshold;
}
if (settings->probing_pkt_out_size > 0) {
engine->default_conn_settings.probing_pkt_out_size = settings->probing_pkt_out_size;
}
}
static const char * const xqc_conn_flag_to_str[XQC_CONN_FLAG_SHIFT_NUM] = {
[XQC_CONN_FLAG_WAIT_WAKEUP_SHIFT] = "WAIT_WAKEUP",
[XQC_CONN_FLAG_HANDSHAKE_COMPLETED_SHIFT] = "HSK_DONE",
[XQC_CONN_FLAG_CAN_SEND_1RTT_SHIFT] = "CAN_SEND_1RTT",
[XQC_CONN_FLAG_TICKING_SHIFT] = "TICKING",
[XQC_CONN_FLAG_ACK_HAS_GAP_SHIFT] = "HAS_GAP",
[XQC_CONN_FLAG_TIME_OUT_SHIFT] = "TIME_OUT",
[XQC_CONN_FLAG_ERROR_SHIFT] = "ERROR",
[XQC_CONN_FLAG_DATA_BLOCKED_SHIFT] = "DATA_BLOCKED",
[XQC_CONN_FLAG_DCID_OK_SHIFT] = "DCID_OK",
[XQC_CONN_FLAG_TOKEN_OK_SHIFT] = "TOKEN_OK",
[XQC_CONN_FLAG_HAS_0RTT_SHIFT] = "HAS_0RTT",
[XQC_CONN_FLAG_0RTT_OK_SHIFT] = "0RTT_OK",
[XQC_CONN_FLAG_0RTT_REJ_SHIFT] = "0RTT_REJECT",
[XQC_CONN_FLAG_UPPER_CONN_EXIST_SHIFT] = "UPPER_CONN_EXIST",
[XQC_CONN_FLAG_INIT_RECVD_SHIFT] = "INIT_RECVD",
[XQC_CONN_FLAG_NEED_RUN_SHIFT] = "NEED_RUN",
[XQC_CONN_FLAG_PING_SHIFT] = "PING",
[XQC_CONN_FLAG_HSK_ACKED_SHIFT] = "HSK_ACKED",
[XQC_CONN_FLAG_RESERVE_SHIFT] = "RESERVE",
[XQC_CONN_FLAG_HANDSHAKE_DONE_RECVD_SHIFT] = "HSK_DONE_RECVD",
[XQC_CONN_FLAG_UPDATE_NEW_TOKEN_SHIFT] = "UPDATE_NEW_TOKEN",
[XQC_CONN_FLAG_VERSION_NEGOTIATION_SHIFT] = "VERSION_NEGOTIATION",
[XQC_CONN_FLAG_HANDSHAKE_CONFIRMED_SHIFT] = "HSK_CONFIRMED",
[XQC_CONN_FLAG_HANDSHAKE_DONE_ACKED_SHIFT] = "HSK_DONE_ACKED",
[XQC_CONN_FLAG_ADDR_VALIDATED_SHIFT] = "ADDR_VALIDATED",
[XQC_CONN_FLAG_LINGER_CLOSING_SHIFT] = "LINGER_CLOSING",
[XQC_CONN_FLAG_RETRY_RECVD_SHIFT] = "RETRY_RECVD",
[XQC_CONN_FLAG_TLS_HSK_COMPLETED_SHIFT] = "TLS_HSK_CMPTD",
[XQC_CONN_FLAG_RECV_NEW_PATH_SHIFT] = "RECV_NEW_PATH",
[XQC_CONN_FLAG_VALIDATE_REBINDING_SHIFT] = "VALIDATE_REBINDING",
[XQC_CONN_FLAG_CONN_CLOSING_NOTIFY_SHIFT] = "CLOSING_NOTIFY",
[XQC_CONN_FLAG_CONN_CLOSING_NOTIFIED_SHIFT] = "CLOSING_NOTIFIED",
[XQC_CONN_FLAG_DGRAM_WAIT_FOR_1RTT_SHIFT] = "DGRAM_WAIT_FOR_1RTT",
[XQC_CONN_FLAG_LOCAL_TP_UPDATED_SHIFT] = "LOCAL_TP_UPDATED",
[XQC_CONN_FLAG_PMTUD_PROBING_SHIFT] = "PMTUD_PROBING",
[XQC_CONN_FLAG_NO_DGRAM_NOTIFIED_SHIFT] = "NO_DGRAM_NOTIFIED",
[XQC_CONN_FLAG_DGRAM_MSS_NOTIFY_SHIFT] = "DGRAM_MSS_NOTIFY",
[XQC_CONN_FLAG_MP_WAIT_MP_READY_SHIFT] = "MP_WAIT_MP_READY",
[XQC_CONN_FLAG_MP_READY_NOTIFY_SHIFT] = "MP_READY_NOTIFY",
};
const char *
xqc_conn_flag_2_str(xqc_connection_t *conn, xqc_conn_flag_t conn_flag)
{
xqc_engine_t *engine = conn->engine;
engine->conn_flag_str_buf[0] = '\0';
size_t pos = 0;
int wsize;
for (int i = 0; i < XQC_CONN_FLAG_SHIFT_NUM; i++) {
if (conn_flag & 1ULL << i) {
wsize = snprintf(engine->conn_flag_str_buf + pos, sizeof(engine->conn_flag_str_buf) - pos, "%s ",
xqc_conn_flag_to_str[i]);
if (wsize < 0 || wsize >= sizeof(engine->conn_flag_str_buf) - pos) {
break;
}
pos += wsize;
}
}
return engine->conn_flag_str_buf;
}
static const char * const xqc_conn_state_to_str[XQC_CONN_STATE_N] = {
[XQC_CONN_STATE_SERVER_INIT] = "S_INIT",
[XQC_CONN_STATE_SERVER_INITIAL_RECVD] = "S_INITIAL_RECVD",
[XQC_CONN_STATE_SERVER_INITIAL_SENT] = "S_INITIAL_SENT",
[XQC_CONN_STATE_SERVER_HANDSHAKE_SENT] = "S_HANDSHAKE_SENT",
[XQC_CONN_STATE_SERVER_HANDSHAKE_RECVD] = "S_HANDSHAKE_RECVD",
[XQC_CONN_STATE_CLIENT_INIT] = "C_INIT",
[XQC_CONN_STATE_CLIENT_INITIAL_RECVD] = "C_INITIAL_RECVD",
[XQC_CONN_STATE_CLIENT_INITIAL_SENT] = "C_INITIAL_SENT",
[XQC_CONN_STATE_CLIENT_HANDSHAKE_SENT] = "C_HANDSHAKE_SENT",
[XQC_CONN_STATE_CLIENT_HANDSHAKE_RECVD] = "C_HANDSHAKE_RECVD",
[XQC_CONN_STATE_ESTABED] = "ESTABED",
[XQC_CONN_STATE_CLOSING] = "CLOSING",
[XQC_CONN_STATE_DRAINING] = "DRAINING",
[XQC_CONN_STATE_CLOSED] = "CLOSED",
};
const char *
xqc_conn_state_2_str(xqc_conn_state_t state)
{
return xqc_conn_state_to_str[state];
}
/* local parameter */
/**
* set settings to default, integer parameters default to be 0,
* while some are defined in [Transport] as non-zero values.
* if a parameter is absent, default value below will be used.
*/
static inline void
xqc_conn_set_default_settings(xqc_trans_settings_t *settings)
{
memset(settings, 0, sizeof(xqc_trans_settings_t));
/* transport parameter related attributes */
settings->max_ack_delay = XQC_DEFAULT_MAX_ACK_DELAY;
settings->ack_delay_exponent = XQC_DEFAULT_ACK_DELAY_EXPONENT;
settings->max_udp_payload_size = XQC_DEFAULT_MAX_UDP_PAYLOAD_SIZE;
settings->active_connection_id_limit = XQC_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
}
static inline void
xqc_conn_init_trans_settings(xqc_connection_t *conn)
{
/* set local and remote settings to default */
xqc_trans_settings_t *ls = &conn->local_settings;
xqc_trans_settings_t *rs = &conn->remote_settings;
uint32_t i, co_bytes;
char *co_str = conn->conn_settings.conn_option_str;
xqc_conn_set_default_settings(ls);
xqc_conn_set_default_settings(rs);
/* set local default setting values */
if (conn->conn_settings.is_interop_mode) {
ls->max_streams_bidi = 128;
ls->max_streams_uni = 128;
} else {
ls->max_streams_bidi = 1024;
ls->max_streams_uni = 1024;
}
ls->max_stream_data_bidi_remote = XQC_MAX_RECV_WINDOW;
ls->max_stream_data_uni = XQC_MAX_RECV_WINDOW;
if (conn->conn_settings.enable_stream_rate_limit) {
ls->max_stream_data_bidi_local = conn->conn_settings.init_recv_window;
} else {
ls->max_stream_data_bidi_local = XQC_MAX_RECV_WINDOW;
}
if (conn->conn_settings.is_interop_mode) {
ls->max_data = 1024 * 1024;
} else {
if (conn->conn_settings.recv_rate_bytes_per_sec) {
ls->max_data = conn->conn_settings.recv_rate_bytes_per_sec * XQC_FC_INIT_RTT / 1000000;
ls->max_data = xqc_max(XQC_MIN_RECV_WINDOW, ls->max_data);
ls->max_data = xqc_min(XQC_MAX_RECV_WINDOW, ls->max_data);
} else {
/* max_data is the sum of stream_data on all uni and bidi streams */
ls->max_data = ls->max_streams_bidi * ls->max_stream_data_bidi_local
+ ls->max_streams_uni * ls->max_stream_data_uni;
}
}
ls->max_idle_timeout = conn->conn_settings.idle_time_out;
ls->max_udp_payload_size = conn->conn_settings.max_udp_payload_size;
ls->active_connection_id_limit = XQC_CONN_ACTIVE_CID_LIMIT;
ls->enable_multipath = conn->conn_settings.enable_multipath;
ls->multipath_version = conn->conn_settings.multipath_version;
ls->init_max_path_id = conn->conn_settings.init_max_path_id;
ls->enable_pmtud = conn->conn_settings.enable_pmtud;
ls->max_datagram_frame_size = conn->conn_settings.max_datagram_frame_size;
ls->disable_active_migration = ls->enable_multipath ? 0 : 1;
ls->max_ack_delay = conn->conn_settings.max_ack_delay;
/* init local conn options */
for (i = 0, co_bytes = 0; i < XQC_CO_STR_MAX_LEN; i++) {
if (co_bytes == 4) {
if ((co_str[i] != ',' && co_str[i] != '\0')) {
// invalid CO. Stop decoding.
break;
} else {
ls->conn_options[ls->conn_option_num++] = XQC_CO_TAG(co_str[i - 4], co_str[i - 3], co_str[i - 2], co_str[i - 1]);
}
co_bytes = 0;
} else {
if (xqc_char_is_letter_or_number(co_str[i])) {
co_bytes++;
} else {
// invalid CO. Stop decoding.
break;
}
}
}
ls->close_dgram_redundancy = conn->conn_settings.close_dgram_redundancy;
#ifdef XQC_ENABLE_FEC
/* init FEC transport params */
if (conn->conn_settings.enable_encode_fec) {
ls->enable_encode_fec = conn->conn_settings.enable_encode_fec;
ls->fec_max_symbols_num = conn->conn_settings.fec_params.fec_max_symbol_num_per_block;
ls->fec_encoder_schemes_num = conn->conn_settings.fec_params.fec_encoder_schemes_num;
for (xqc_int_t i = 0; i < conn->conn_settings.fec_params.fec_encoder_schemes_num; i++) {
ls->fec_encoder_schemes[i] = conn->conn_settings.fec_params.fec_encoder_schemes[i];
}
}
if (conn->conn_settings.enable_decode_fec) {
ls->enable_decode_fec = conn->conn_settings.enable_decode_fec;
ls->fec_decoder_schemes_num = conn->conn_settings.fec_params.fec_decoder_schemes_num;
for (xqc_int_t i = 0; i < conn->conn_settings.fec_params.fec_decoder_schemes_num; i++) {
ls->fec_decoder_schemes[i] = conn->conn_settings.fec_params.fec_decoder_schemes[i];
}
}
#endif
}
void
xqc_conn_init_flow_ctl(xqc_connection_t *conn)
{
xqc_conn_flow_ctl_t *flow_ctl = &conn->conn_flow_ctl;
xqc_trans_settings_t * settings = & conn->local_settings;
/* TODO: send params are inited to be zero, until zerortt inited or handshake done */
flow_ctl->fc_max_data_can_send = settings->max_data; /* replace with the value specified by peer after handshake */
flow_ctl->fc_max_data_can_recv = settings->max_data;
flow_ctl->fc_max_streams_bidi_can_send = settings->max_streams_bidi; /* replace with the value specified by peer after handshake */
flow_ctl->fc_max_streams_bidi_can_recv = settings->max_streams_bidi;
flow_ctl->fc_max_streams_uni_can_send = settings->max_streams_uni; /* replace with the value specified by peer after handshake */
flow_ctl->fc_max_streams_uni_can_recv = settings->max_streams_uni;
flow_ctl->fc_data_sent = 0;
flow_ctl->fc_data_recved = 0;
flow_ctl->fc_recv_windows_size = settings->max_data;
flow_ctl->fc_last_window_update_time = 0;
}
static inline void
xqc_conn_init_key_update_ctx(xqc_connection_t *conn)
{
xqc_key_update_ctx_t *ctx = &conn->key_update_ctx;
ctx->cur_out_key_phase = 0;
ctx->next_in_key_phase = 0;
ctx->first_sent_pktno = 0;
ctx->first_recv_pktno = 0;
ctx->enc_pkt_cnt = 0;
ctx->initiate_time_guard = 0;
}
static inline void
xqc_conn_init_timer_manager(xqc_connection_t *conn)
{
xqc_timer_manager_t *timer_manager = &conn->conn_timer_manager;
xqc_usec_t now = xqc_monotonic_timestamp();
xqc_timer_init(timer_manager, conn->log, conn);
xqc_timer_set(timer_manager, XQC_TIMER_CONN_IDLE, now, xqc_conn_get_idle_timeout(conn) * 1000);
if (conn->conn_settings.ping_on
&& conn->conn_type == XQC_CONN_TYPE_CLIENT)
{
xqc_timer_set(timer_manager, XQC_TIMER_PING, now, XQC_PING_TIMEOUT * 1000);
}
}
static void
xqc_conn_set_ack_ext_local_settings(xqc_trans_settings_t *local_settings, const xqc_conn_settings_t *settings)
{
if (settings->extended_ack_features & XQC_ACK_EXT_FEATURE_BIT_RECV_TS) {
local_settings->extended_ack_features = 2;
local_settings->max_receive_timestamps_per_ack = settings->max_receive_timestamps_per_ack;
local_settings->receive_timestamps_exponent = settings->receive_timestamps_exponent;
}
}
xqc_connection_t *
xqc_conn_create(xqc_engine_t *engine, xqc_cid_t *dcid, xqc_cid_t *scid,
const xqc_conn_settings_t *settings, void *user_data, xqc_conn_type_t type)
{
xqc_connection_t *xc = NULL;
#ifdef XQC_PROTECT_POOL_MEM
xqc_memory_pool_t *pool = xqc_create_pool(engine->config->conn_pool_size, settings->protect_pool_mem);
#else
xqc_memory_pool_t *pool = xqc_create_pool(engine->config->conn_pool_size);
#endif
if (pool == NULL) {
return NULL;
}
#ifdef XQC_PROTECT_POOL_MEM
xqc_log(engine->log, XQC_LOG_DEBUG, "|mempool|protect:%d|page_sz:%z|",
pool->protect_block, pool->page_size);
#endif
xc = xqc_pcalloc(pool, sizeof(xqc_connection_t));
if (xc == NULL) {
goto fail;
}
xc->conn_settings = *settings;
xqc_memcpy(xc->conn_settings.conn_option_str, settings->conn_option_str, XQC_CO_STR_MAX_LEN);
xqc_conn_set_default_sched_params(&engine->default_conn_settings, &xc->conn_settings);
if (xc->conn_settings.max_udp_payload_size == 0) {
xc->conn_settings.max_udp_payload_size = engine->default_conn_settings.max_udp_payload_size;
}
if (xc->conn_settings.initial_rtt == 0) {
xc->conn_settings.initial_rtt = XQC_kInitialRtt_us;
}
if (xc->conn_settings.max_ack_delay == 0) {
xc->conn_settings.max_ack_delay = XQC_DEFAULT_MAX_ACK_DELAY;
}
xc->conn_settings.max_ack_delay = xqc_min(xc->conn_settings.max_ack_delay, XQC_DEFAULT_MAX_ACK_DELAY);
if (xc->conn_settings.datagram_redundant_probe) {
xc->conn_settings.datagram_redundant_probe = xqc_max(xc->conn_settings.datagram_redundant_probe,
XQC_MIN_DATAGRAM_REDUNDANT_PROBE_INTERVAL);
}
if (xc->conn_settings.mp_enable_reinjection & XQC_REINJ_UNACK_BEFORE_SCHED) {
xc->conn_settings.mp_enable_reinjection |= XQC_REINJ_UNACK_AFTER_SEND;
}
if (xc->conn_settings.datagram_redundancy > XQC_MAX_DATAGRAM_REDUNDANCY) {
xc->conn_settings.datagram_redundancy = XQC_MAX_DATAGRAM_REDUNDANCY;
}
if (xc->conn_settings.datagram_redundancy) {
if (xc->conn_settings.datagram_redundancy == 1) {
/* reinject packets on any path */
xc->conn_settings.scheduler_callback = xqc_rap_scheduler_cb;
} else {
/* do not reinject packets on the same path */
xc->conn_settings.scheduler_callback = xqc_minrtt_scheduler_cb;
}
xc->conn_settings.reinj_ctl_callback = xqc_dgram_reinj_ctl_cb;
xc->conn_settings.mp_enable_reinjection |= XQC_REINJ_UNACK_AFTER_SEND;
}
if (xc->conn_settings.init_recv_window) {
xc->conn_settings.init_recv_window = xqc_max(xc->conn_settings.init_recv_window, XQC_QUIC_MAX_MSS);
} else {
xc->conn_settings.init_recv_window = XQC_MIN_RECV_WINDOW;
}
if (xc->conn_settings.standby_path_probe_timeout) {
xc->conn_settings.standby_path_probe_timeout = xqc_max(xc->conn_settings.standby_path_probe_timeout, XQC_MIN_STANDBY_RPOBE_TIMEOUT);
}
if (xc->conn_settings.max_pkt_out_size == 0) {
xc->conn_settings.max_pkt_out_size = engine->default_conn_settings.max_pkt_out_size;
}
if (xc->conn_settings.max_pkt_out_size > XQC_MAX_PACKET_OUT_SIZE) {
xc->conn_settings.max_pkt_out_size = XQC_MAX_PACKET_OUT_SIZE;
}
if (xc->conn_settings.pmtud_probing_interval == 0) {
xc->conn_settings.pmtud_probing_interval = engine->default_conn_settings.pmtud_probing_interval;
}
if (xc->conn_settings.ack_frequency == 0) {
xc->conn_settings.ack_frequency = engine->default_conn_settings.ack_frequency;
}
if (xc->conn_settings.pto_backoff_factor == 0) {
xc->conn_settings.pto_backoff_factor = engine->default_conn_settings.pto_backoff_factor;
}
if (xc->conn_settings.loss_detection_pkt_thresh == 0) {
xc->conn_settings.loss_detection_pkt_thresh = engine->default_conn_settings.loss_detection_pkt_thresh;
}
xc->version = (type == XQC_CONN_TYPE_CLIENT) ? settings->proto_version : XQC_IDRAFT_INIT_VER;
if (type == XQC_CONN_TYPE_CLIENT
&& !xqc_check_proto_version_valid(settings->proto_version))
{
xc->conn_settings.proto_version = XQC_VERSION_V1;
xc->version = XQC_VERSION_V1;
}
/* make sure a 0-value config will not result in immediate timeout */
if (xc->conn_settings.init_idle_time_out == 0) {
xc->conn_settings.init_idle_time_out = XQC_CONN_INITIAL_IDLE_TIMEOUT;
}
if (xc->conn_settings.idle_time_out == 0) {
xc->conn_settings.idle_time_out = XQC_CONN_DEFAULT_IDLE_TIMEOUT;
}
if (xc->conn_settings.anti_amplification_limit == 0) {
xc->conn_settings.anti_amplification_limit = XQC_DEFAULT_ANTI_AMPLIFICATION_LIMIT;
}
if (xc->conn_settings.reinj_flexible_deadline_srtt_factor == 0) {
xc->conn_settings.reinj_flexible_deadline_srtt_factor = engine->default_conn_settings.reinj_flexible_deadline_srtt_factor;
}
if (xc->conn_settings.reinj_hard_deadline == 0) {
xc->conn_settings.reinj_hard_deadline = engine->default_conn_settings.reinj_hard_deadline;
}
if (xqc_conn_is_current_mp_version_supported(xc->conn_settings.multipath_version) != XQC_OK) {
xc->conn_settings.multipath_version = XQC_MULTIPATH_10;
}
if (xc->conn_settings.init_max_path_id == 0) {
xc->conn_settings.init_max_path_id = XQC_DEFAULT_INIT_MAX_PATH_ID;
}
if (xc->conn_settings.probing_pkt_out_size == 0) {
xc->conn_settings.probing_pkt_out_size = engine->default_conn_settings.probing_pkt_out_size;
}
xc->conn_settings.extended_ack_features = 0;
xc->conn_settings.max_receive_timestamps_per_ack = 0;
xc->conn_settings.receive_timestamps_exponent = 0;
#ifdef XQC_ENABLE_FEC
xc->fec_neg_fail_reason = 0;
if (xc->conn_settings.enable_encode_fec) {
if (xc->conn_settings.fec_params.fec_code_rate == 0) {
xc->conn_settings.fec_params.fec_code_rate = engine->default_conn_settings.fec_params.fec_code_rate;
}
if (xc->conn_settings.fec_params.fec_ele_bit_size == 0) {
xc->conn_settings.fec_params.fec_ele_bit_size = engine->default_conn_settings.fec_params.fec_ele_bit_size;
}
if (xc->conn_settings.fec_params.fec_protected_frames == 0) {
xc->conn_settings.fec_params.fec_protected_frames = engine->default_conn_settings.fec_params.fec_protected_frames;
}
if (xc->conn_settings.fec_params.fec_max_symbol_num_per_block == 0) {
xc->conn_settings.fec_params.fec_max_symbol_num_per_block = engine->default_conn_settings.fec_params.fec_max_symbol_num_per_block;
}
if (xc->conn_settings.fec_params.fec_mp_mode == 0) {
xc->conn_settings.fec_params.fec_mp_mode = engine->default_conn_settings.fec_params.fec_mp_mode;
}
if (xc->conn_settings.fec_level == 0) {
xc->conn_settings.fec_level = engine->default_conn_settings.fec_level;
}
}
if (xc->conn_settings.enable_decode_fec) {
if (xc->conn_settings.fec_params.fec_max_window_size) {
xc->conn_settings.fec_params.fec_max_window_size = xqc_min(xc->conn_settings.fec_params.fec_max_window_size, XQC_SYMBOL_CACHE_LEN);
} else {
xc->conn_settings.fec_params.fec_max_window_size = engine->default_conn_settings.fec_params.fec_max_window_size;
}
if (xc->conn_settings.fec_params.fec_blk_log_mod == 0) {
xc->conn_settings.fec_params.fec_blk_log_mod = engine->default_conn_settings.fec_params.fec_blk_log_mod;
}
if (xc->conn_settings.fec_params.fec_log_on == 0) {
xc->conn_settings.fec_params.fec_log_on = engine->default_conn_settings.fec_params.fec_log_on;
}
if (xc->conn_settings.fec_params.fec_packet_mask_mode == 0) {
xc->conn_settings.fec_params.fec_packet_mask_mode = engine->default_conn_settings.fec_params.fec_packet_mask_mode;
}
}
if (xc->conn_settings.fec_conn_queue_rpr_timeout == 0) {
xc->conn_settings.fec_conn_queue_rpr_timeout = engine->default_conn_settings.fec_conn_queue_rpr_timeout;
}
if (xc->conn_settings.enable_encode_fec
|| xc->conn_settings.enable_decode_fec)
{
xc->fec_ctl = xqc_fec_ctl_create(xc);
if (xc->fec_ctl == NULL) {
xc->conn_settings.enable_encode_fec = 0;
xc->conn_settings.enable_decode_fec = 0;
}
}
#endif
xqc_conn_init_trans_settings(xc);
xqc_conn_set_ack_ext_local_settings(&xc->local_settings, settings);
xqc_conn_init_flow_ctl(xc);
xqc_conn_init_key_update_ctx(xc);
xc->conn_pool = pool;
xqc_init_cid_set(&xc->dcid_set);
xqc_init_cid_set(&xc->scid_set);
if (xqc_cid_set_add_path(&xc->dcid_set, XQC_INITIAL_PATH_ID) != XQC_OK) {
goto fail;
}
if (xqc_cid_set_add_path(&xc->scid_set, XQC_INITIAL_PATH_ID) != XQC_OK) {
goto fail;
}
xqc_cid_set_update_state(&xc->dcid_set, XQC_INITIAL_PATH_ID, XQC_CID_SET_USED);
xqc_cid_set_update_state(&xc->scid_set, XQC_INITIAL_PATH_ID, XQC_CID_SET_USED);
if (xqc_cid_set_insert_cid(&xc->dcid_set, dcid, XQC_CID_USED,
xc->local_settings.active_connection_id_limit,
XQC_INITIAL_PATH_ID))
{
goto fail;
}
xqc_cid_copy(&(xc->dcid_set.current_dcid), dcid);
xqc_hex_dump(xc->dcid_set.current_dcid_str, dcid->cid_buf, dcid->cid_len);
xc->dcid_set.current_dcid_str[dcid->cid_len * 2] = '\0';
if (xqc_cid_set_insert_cid(&xc->scid_set, scid, XQC_CID_USED,
xc->remote_settings.active_connection_id_limit,
XQC_INITIAL_PATH_ID))
{
goto fail;
}
xqc_cid_copy(&(xc->scid_set.user_scid), scid);
xqc_hex_dump(xc->scid_set.original_scid_str, scid->cid_buf, scid->cid_len);
xc->scid_set.original_scid_str[scid->cid_len * 2] = '\0';
xqc_cid_set_set_largest_seq_or_rpt(&xc->scid_set, XQC_INITIAL_PATH_ID, scid->cid_seq_num);
xqc_cid_copy(&(xc->initial_scid), scid);
xc->engine = engine;
xc->log = xqc_log_init(engine->log->log_level, engine->log->log_event, engine->log->qlog_importance, engine->log->log_timestamp,
engine->log->log_level_name, engine, engine->log->log_callbacks, engine->log->user_data);
xc->log->scid = xc->scid_set.original_scid_str;
xc->transport_cbs = engine->transport_cbs;
xc->user_data = user_data;
xc->discard_vn_flag = 0;
xc->conn_type = type;
xc->conn_flag = 0;
xc->conn_state = (type == XQC_CONN_TYPE_SERVER) ? XQC_CONN_STATE_SERVER_INIT : XQC_CONN_STATE_CLIENT_INIT;
xqc_log_event(xc->log, CON_CONNECTION_STATE_UPDATED, xc);
xc->zero_rtt_count = 0;
xc->conn_create_time = xqc_monotonic_timestamp();
xc->handshake_complete_time = 0;
xc->first_data_send_time = 0;
xc->max_stream_id_bidi_remote = -1;
xc->max_stream_id_uni_remote = -1;
xc->last_dgram = NULL;
xc->pkt_out_size = xqc_min(xc->conn_settings.max_pkt_out_size, xc->conn_settings.max_udp_payload_size - XQC_PACKET_OUT_EXT_SPACE);
xc->max_pkt_out_size = xc->conn_settings.probing_pkt_out_size;
xc->probing_pkt_out_size = xc->conn_settings.probing_pkt_out_size;
xc->probing_cnt = 0;
for (xqc_encrypt_level_t encrypt_level = XQC_ENC_LEV_INIT; encrypt_level < XQC_ENC_LEV_MAX; encrypt_level++) {
xc->undecrypt_count[encrypt_level] = 0;
}
xc->enc_pkt = xqc_malloc(XQC_PACKET_OUT_BUF_CAP);
if (NULL == xc->enc_pkt) {
xqc_log(xc->log, XQC_LOG_ERROR, "|malloc enc pkt buf fail");
goto fail;
}
xc->enc_pkt_cap = XQC_PACKET_OUT_BUF_CAP;
xc->conn_send_queue = xqc_send_queue_create(xc);
if (xc->conn_send_queue == NULL) {
goto fail;
}
xqc_conn_init_timer_manager(xc);
if (xc->conn_settings.datagram_redundant_probe) {
xc->last_dgram = xqc_var_buf_create_with_limit(XQC_MAX_PACKET_OUT_SIZE, XQC_MAX_PACKET_OUT_SIZE);
if (xc->last_dgram == NULL) {
goto fail;
}
xc->dgram_probe_timer = xqc_conn_register_gp_timer(xc, "dgram_probe", xqc_conn_dgram_probe_timeout, xc);
if (xc->dgram_probe_timer < 0) {
xqc_log(xc->log, XQC_LOG_ERROR, "|register dgram probe timer error|");
goto fail;
}
}
xqc_init_list_head(&xc->conn_write_streams);
xqc_init_list_head(&xc->conn_read_streams);
xqc_init_list_head(&xc->conn_closing_streams);
xqc_init_list_head(&xc->conn_all_streams);
xqc_init_list_head(&xc->initial_crypto_data_list);
xqc_init_list_head(&xc->hsk_crypto_data_list);
xqc_init_list_head(&xc->application_crypto_data_list);
for (xqc_encrypt_level_t encrypt_level = XQC_ENC_LEV_INIT; encrypt_level < XQC_ENC_LEV_MAX; encrypt_level++) {
xqc_init_list_head(&xc->undecrypt_packet_in[encrypt_level]);
}
xc->crypto_data_total_len = 0;
/* create streams_hash */
xc->streams_hash = xqc_pcalloc(xc->conn_pool, sizeof(xqc_id_hash_table_t));
if (xc->streams_hash == NULL) {
goto fail;
}
if (xqc_id_hash_init(xc->streams_hash,
xqc_default_allocator,
engine->config->streams_hash_bucket_size) == XQC_ERROR) {
goto fail;
}
xc->passive_streams_hash = xqc_pcalloc(xc->conn_pool, sizeof(xqc_id_hash_table_t));
if (xc->passive_streams_hash == NULL) {
goto fail;
}
if (xqc_id_hash_init(xc->passive_streams_hash, xqc_default_allocator,
engine->config->streams_hash_bucket_size) == XQC_ERROR) {
goto fail;
}
/* insert into engine's conns_hash */
if (xqc_insert_conns_hash(engine->conns_hash, xc,
xc->scid_set.user_scid.cid_buf,
xc->scid_set.user_scid.cid_len))
{
goto fail;
}
/* set scheduler callback (default: minRTT) */
if (xc->conn_settings.scheduler_callback.xqc_scheduler_init) {
xc->scheduler_callback = &xc->conn_settings.scheduler_callback;
} else {
xc->scheduler_callback = &xqc_minrtt_scheduler_cb;
}
xc->scheduler = xqc_pcalloc(xc->conn_pool, xc->scheduler_callback->xqc_scheduler_size());
if (xc->scheduler == NULL) {
goto fail;
}
xc->scheduler_callback->xqc_scheduler_init(xc->scheduler, xc->log, &xc->conn_settings.scheduler_params);
/* set reinject control callback if reinjection enabled */
if (xc->conn_settings.reinj_ctl_callback.xqc_reinj_ctl_init) {
xc->reinj_callback = &xc->conn_settings.reinj_ctl_callback;
xc->reinj_ctl = xqc_pcalloc(xc->conn_pool, xc->reinj_callback->xqc_reinj_ctl_size());
if (xc->reinj_ctl == NULL) {
goto fail;
}
xc->reinj_callback->xqc_reinj_ctl_init(xc->reinj_ctl, xc);
}
/*
* Init paths after the scheduler and the reinjection controller are initialized.
*/
if (xqc_conn_init_paths_list(xc) != XQC_OK) {
goto fail;
}
xc->pkt_filter_cb = NULL;
/* for datagram */
xc->next_dgram_id = 0;
xqc_init_list_head(&xc->dgram_0rtt_buffer_list);
xqc_init_list_head(&xc->ping_notification_list);
xqc_log(xc->log, XQC_LOG_DEBUG, "|success|scid:%s|dcid:%s|conn:%p|",
xqc_scid_str(engine, &xc->scid_set.user_scid), xqc_dcid_str(engine, &xc->dcid_set.current_dcid), xc);
xqc_log_event(xc->log, TRA_PARAMETERS_SET, xc, XQC_LOG_LOCAL_EVENT);
return xc;
fail:
if (xc != NULL) {
xqc_conn_destroy(xc);
}
return NULL;
}
xqc_int_t
xqc_conn_encode_local_tp(xqc_connection_t *conn, uint8_t *dst, size_t dst_cap, size_t *dst_len)
{
xqc_transport_params_t params;
memset(¶ms, 0, sizeof(xqc_transport_params_t));
xqc_transport_params_type_t tp_type =
(conn->conn_type == XQC_CONN_TYPE_CLIENT ? XQC_TP_TYPE_CLIENT_HELLO :
XQC_TP_TYPE_ENCRYPTED_EXTENSIONS);
/* get local transport params */
xqc_int_t ret = xqc_conn_get_local_transport_params(conn, ¶ms);
if (ret != XQC_OK) {
return ret;
}
/* serialize transport params */
ret = xqc_encode_transport_params(¶ms, tp_type, dst, dst_cap, dst_len);
if (ret != XQC_OK) {
xqc_log(conn->log, XQC_LOG_ERROR, "|encode tls trans param error|ret:%d", ret);
return ret;
}
return XQC_OK;
}
xqc_int_t
xqc_conn_create_server_tls(xqc_connection_t *conn)
{
xqc_int_t ret;
/* init cfg */
xqc_tls_config_t cfg = {0};
char tp_buf[XQC_MAX_TRANSPORT_PARAM_BUF_LEN] = {0};
cfg.trans_params = tp_buf;
/* encode local transport parameters, and set to tls config */
ret = xqc_conn_encode_local_tp(conn, cfg.trans_params,
XQC_MAX_TRANSPORT_PARAM_BUF_LEN, &cfg.trans_params_len);
if (ret != XQC_OK) {
return ret;
}
/* create tls instance */
conn->tls = xqc_tls_create(conn->engine->tls_ctx, &cfg, conn->log, conn);
if (NULL == conn->tls) {
xqc_log(conn->log, XQC_LOG_ERROR, "|create tls instance error");
return -XQC_EMALLOC;
}
return XQC_OK;
}
xqc_connection_t *
xqc_conn_server_create(xqc_engine_t *engine, const struct sockaddr *local_addr,
socklen_t local_addrlen, const struct sockaddr *peer_addr, socklen_t peer_addrlen,
xqc_cid_t *dcid, xqc_cid_t *scid, xqc_conn_settings_t *settings, void *user_data)
{
xqc_int_t ret;
xqc_connection_t *conn;
xqc_cid_t new_scid;
xqc_cid_copy(&new_scid, scid);
/*
* Server enable cid negotiate, or client initial dcid length not equal to server config length.
* If use the peer's dcid as scid directly, must make sure
* its length equals to the config cid_len, otherwise might fail
* decoding dcid from subsequent short header packets
*/
if (engine->config->cid_negotiate
|| new_scid.cid_len != engine->config->cid_len)
{
/* server generates it's own cid */
if (xqc_generate_cid(engine, scid, &new_scid, 0) != XQC_OK) {
xqc_log(engine->log, XQC_LOG_ERROR, "|fail to generate_cid|");
return NULL;
}
}
/* generate sr token for server's initial cid */
xqc_gen_reset_token(&new_scid, new_scid.sr_token,
XQC_STATELESS_RESET_TOKENLEN,
engine->config->reset_token_key,
engine->config->reset_token_keylen);
conn = xqc_conn_create(engine, dcid, &new_scid, settings, user_data, XQC_CONN_TYPE_SERVER);
if (conn == NULL) {
xqc_log(engine->log, XQC_LOG_ERROR, "|fail to create connection|");
return NULL;
}
xqc_cid_copy(&conn->original_dcid, scid);
if (xqc_cid_in_cid_set(&conn->scid_set, &conn->original_dcid, XQC_INITIAL_PATH_ID) == NULL) {
/*
* if server choose it's own cid, then if server Initial is lost,
* and if client Initial retransmit, server might use odcid to
* find the created conn
*/
if (xqc_insert_conns_hash(engine->conns_hash, conn,
conn->original_dcid.cid_buf,
conn->original_dcid.cid_len))
{
goto fail;
}
xqc_log(conn->log, XQC_LOG_INFO, "|hash odcid conn|odcid:%s|conn:%p|",
xqc_dcid_str(engine, &conn->original_dcid), conn);
}
ret = xqc_memcpy_with_cap(conn->local_addr, sizeof(conn->local_addr),
local_addr, local_addrlen);
if (ret == XQC_OK) {
conn->local_addrlen = local_addrlen;
} else {
xqc_log(conn->log, XQC_LOG_ERROR,
"|local addr too large|addr_len:%d|",
(int)local_addrlen);
goto fail;
}
ret = xqc_memcpy_with_cap(conn->peer_addr, sizeof(conn->peer_addr),
peer_addr, peer_addrlen);
if (ret == XQC_OK) {
conn->peer_addrlen = peer_addrlen;
} else {
xqc_log(conn->log, XQC_LOG_ERROR,
"|peer addr too large|addr_len:%d|",
(int)peer_addrlen);
goto fail;
}
ret = xqc_conn_create_server_tls(conn);
if (ret != XQC_OK) {
goto fail;
}
ret = xqc_conn_server_init_path_addr(conn, XQC_INITIAL_PATH_ID,
local_addr, local_addrlen,
peer_addr, peer_addrlen);
if (ret != XQC_OK) {
goto fail;
}
xqc_log_event(conn->log, CON_CONNECTION_STARTED, conn, XQC_LOG_REMOTE_EVENT);
if (conn->transport_cbs.server_accept) {
if (conn->transport_cbs.server_accept(engine, conn, &conn->scid_set.user_scid, user_data) < 0) {
xqc_log(engine->log, XQC_LOG_ERROR, "|server_accept callback return error|");
XQC_CONN_ERR(conn, TRA_CONNECTION_REFUSED_ERROR);
goto fail;
}
conn->conn_flag |= XQC_CONN_FLAG_UPPER_CONN_EXIST;
}
return conn;
fail:
xqc_conn_destroy(conn);
return NULL;
}
xqc_int_t
xqc_conn_client_on_alpn(xqc_connection_t *conn, const unsigned char *alpn, size_t alpn_len)
{
xqc_int_t ret;
/* save alpn */
conn->alpn = xqc_calloc(1, alpn_len + 1);
if (conn->alpn == NULL) {
xqc_log(conn->log, XQC_LOG_ERROR, "|malloc alpn buffer error|");
return -XQC_EMALLOC;
}
xqc_memcpy(conn->alpn, alpn, alpn_len);
conn->alpn_len = alpn_len;
/* set quic callbacks to quic connection */
ret = xqc_engine_get_alpn_callbacks(conn->engine, alpn, alpn_len, &conn->app_proto_cbs);
if (ret != XQC_OK) {
xqc_free(conn->alpn);
conn->alpn = NULL;
conn->alpn_len = 0;
xqc_log(conn->log, XQC_LOG_ERROR, "|can't get application layer callback|ret:%d", ret);
return ret;
}
return XQC_OK;
}
xqc_int_t
xqc_conn_server_on_alpn(xqc_connection_t *conn, const unsigned char *alpn, size_t alpn_len)
{
xqc_int_t ret;
/* save alpn */
conn->alpn = xqc_calloc(1, alpn_len + 1);
if (conn->alpn == NULL) {
xqc_log(conn->log, XQC_LOG_ERROR, "|malloc alpn buffer error|");
return -XQC_EMALLOC;
}
xqc_memcpy(conn->alpn, alpn, alpn_len);
conn->alpn_len = alpn_len;
/* set quic callbacks to quic connection */
ret = xqc_engine_get_alpn_callbacks(conn->engine, alpn, alpn_len, &conn->app_proto_cbs);
if (ret != XQC_OK) {
xqc_free(conn->alpn);
conn->alpn = NULL;
conn->alpn_len = 0;
xqc_log(conn->log, XQC_LOG_ERROR, "|can't get application layer callback|ret:%d", ret);
return ret;
}
uint8_t tp_buf[XQC_MAX_TRANSPORT_PARAM_BUF_LEN] = {0};
size_t tp_len = 0;
/* do callback */
if (conn->app_proto_cbs.conn_cbs.conn_create_notify) {
if (conn->app_proto_cbs.conn_cbs.conn_create_notify(conn, &conn->scid_set.user_scid,
conn->user_data, conn->proto_data))
{
goto err;
}
conn->conn_flag |= XQC_CONN_FLAG_UPPER_CONN_EXIST;
}
xqc_datagram_record_mss(conn);
if (conn->conn_flag & XQC_CONN_FLAG_LOCAL_TP_UPDATED) {
ret = xqc_conn_encode_local_tp(conn, tp_buf,
XQC_MAX_TRANSPORT_PARAM_BUF_LEN, &tp_len);
if (ret != XQC_OK) {
xqc_log(conn->log, XQC_LOG_ERROR, "|server encode tp error|ret:%d|", ret);
goto err;
}
ret = xqc_tls_update_tp(conn->tls, tp_buf, tp_len);
if (ret != XQC_OK) {
xqc_log(conn->log, XQC_LOG_ERROR, "|server tls update tp error|ret:%d|", ret);
goto err;
}
conn->conn_flag &= ~XQC_CONN_FLAG_LOCAL_TP_UPDATED;
xqc_log(conn->log, XQC_LOG_INFO,
"|update tp|max_datagram_frame_size:%ud|",
conn->local_settings.max_datagram_frame_size);
}
return XQC_OK;
err:
XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR);
return -TRA_INTERNAL_ERROR;
}
#ifdef XQC_ENABLE_FEC
void
xqc_fec_conn_info_print(xqc_connection_t *conn, char *output, size_t output_size)
{
int32_t i, ret;
size_t curr_size;
i = ret = 0;
curr_size = 0;
xqc_memset(output, 0, output_size);
if (conn->fec_ctl == NULL) {
goto full;
}
/* fec info, must be ended with ',' */
ret = snprintf(output, output_size,
"%u,%u,%u,%u,%u,%u,%u,%u,%u,%s,%s,%d,%"PRIu64",%"PRIu64","
,
conn->conn_settings.fec_params.fec_encoder_scheme ? 1 : 0,
conn->conn_settings.fec_params.fec_decoder_scheme ? 1 : 0,
conn->fec_ctl->fec_processed_blk_num > 0 ? 1 : 0,
conn->fec_ctl->fec_recover_pkt_cnt,
conn->fec_ctl->fec_recover_failed_cnt,
conn->fec_ctl->fec_flush_blk_cnt,
conn->fec_ctl->fec_recv_repair_num_total,
conn->fec_neg_fail_reason,
conn->fec_ctl->fec_send_ahead,
xqc_get_fec_scheme_str(conn->conn_settings.fec_params.fec_encoder_scheme),
xqc_get_fec_scheme_str(conn->conn_settings.fec_params.fec_decoder_scheme),
conn->fec_ctl->fec_enable_stream_num,
conn->fec_ctl->conn_avg_recv_delay / 1000,
conn->fec_ctl->fec_avg_opt_time / 1000
);
curr_size += ret;
if (curr_size >= output_size) {
goto full;
}
full:
curr_size = xqc_min(curr_size, output_size);
for (i = curr_size - 1; i >= 0; i--) {
if (output[i] == ',') {
output[i] = '\0';
break;
}
}
output[output_size - 1] = '\0';
}
#endif
void
xqc_common_conn_info_print(xqc_connection_t *conn, char *output, size_t output_size)
{
int32_t i, ret;
size_t curr_size;;
i = ret = 0;
curr_size = 0;
xqc_memset(output, 0, output_size);
/* common info, must be ended with ',' */
ret = snprintf(output, output_size,
"%d,%"PRIu64",%"PRIu64",%"PRIu64",%d,"
,
conn->conn_video_frames,
conn->conn_avg_close_delay / 1000,
conn->conn_avg_recv_delay / 1000,
conn->conn_latest_close_delay / 1000,
conn->burst_loss_cnt
);
curr_size += ret;
if (curr_size >= output_size) {
goto full;
}
full:
curr_size = xqc_min(curr_size, output_size);
for (i = curr_size - 1; i >= 0; i--) {
if (output[i] == ',') {
output[i] = '\0';
break;
}
}
output[output_size - 1] = '\0';
}
void
xqc_extern_conn_info_print(xqc_connection_t *conn, xqc_conn_stats_t *conn_stats)
{
int32_t i, ret;
char *buff = conn_stats->extern_conn_info;
size_t curr_size, buff_size;
#ifdef XQC_ENABLE_FEC
char fec_buf[XQC_MAX_FEC_BUF_LEN];
xqc_fec_conn_info_print(conn, fec_buf, XQC_MAX_FEC_BUF_LEN);
#endif
char common_buf[XQC_MAX_COMMON_BUF_LEN];
xqc_common_conn_info_print(conn, common_buf, XQC_MAX_COMMON_BUF_LEN);
i = ret = 0;
curr_size = 0;
buff_size = XQC_EXTERN_CONN_INFO_LEN;
/* conn info */
ret = snprintf(buff, buff_size,
"{"
#ifdef XQC_ENABLE_FEC
"\"fec\": \"%s\","
#endif
"\"common\": \"%s\","
"}"
#ifdef XQC_ENABLE_FEC
,
fec_buf
#endif
,
common_buf
);
curr_size += ret;
if (curr_size >= buff_size) {
goto full;
}
full:
curr_size = xqc_min(curr_size, buff_size);
buff[curr_size] = '\0';
}
void
xqc_conn_destroy(xqc_connection_t *xc)
{
static const char *empty = "";
const char *out_alpn = NULL;
size_t out_alpn_len = 0;
if (!xc) {
return;
}
if (xc->conn_flag & XQC_CONN_FLAG_TICKING) {
xqc_log(xc->log, XQC_LOG_ERROR, "|in XQC_CONN_FLAG_TICKING|%p|", xc);
xc->conn_state = XQC_CONN_STATE_CLOSED;
xqc_log_event(xc->log, CON_CONNECTION_STATE_UPDATED, xc);
return;
}
uint8_t fec_flag;
xqc_conn_stats_t conn_stats;
xqc_fec_schemes_e ensch, desch;
unsigned char *ensch_str, *desch_str, *fec_mpm_str;
fec_flag = 0;
ensch = xc->conn_settings.fec_params.fec_encoder_scheme;
desch = xc->conn_settings.fec_params.fec_decoder_scheme;
ensch_str = desch_str = fec_mpm_str = "NO_FEC";
xqc_memzero(&conn_stats, sizeof(xqc_conn_stats_t));
xqc_conn_get_stats_internal(xc, &conn_stats);
#ifdef XQC_ENABLE_FEC
fec_flag = 1;
ensch_str = xqc_get_fec_scheme_str(ensch);
desch_str = xqc_get_fec_scheme_str(desch);
fec_mpm_str = xqc_get_fec_mp_mode_str(xc->fec_ctl);
#endif
if (xc->tls) {
xqc_tls_get_selected_alpn(xc->tls, &out_alpn, &out_alpn_len);
}
if (out_alpn == NULL) {
out_alpn = empty;
out_alpn_len = 0;
}
xqc_log(xc->log, XQC_LOG_REPORT, "|%p|"
"has_0rtt:%d|0rtt_accept:%d|token_ok:%d|handshake_time:%ui|"
"first_send_delay:%ui|conn_persist:%ui|keyupdate_cnt:%d|err:0x%xi|close_msg:%s|%s|"
"hsk_recv:%ui|close_recv:%ui|close_send:%ui|last_recv:%ui|last_send:%ui|"
"mp_enable:%ud|create:%ud|validated:%ud|active:%ud|path_info:%s|alpn:%*s|rebind_count:%d|"
"rebind_valid:%d|rtx_pkt:%ud|tlp_pkt:%ud|"
"snd_pkt:%ud|spurious_loss:%ud|detected_loss:%ud|"
"max_pto:%ud|finished_streams:%ud|cli_bidi_s:%ud|svr_bidi_s:%ud|"
"fec_exist:%ud|"
"fec_ensch:%s|fec_desch:%s|fec_neg_fail:%ud|"
"fec_mp_mode:%s|send_fec_pkts:%ud|recovered_fec_num:%ud|"
"max_po_size:%uz|max_probing_size:%uz|ppo_size:%uz|"
"ext_conn_info:%s|max_acked_po_size:%uz|enable_pmtud:%ui|avg_closed_time:%ui|"
,
xc,
xc->conn_flag & XQC_CONN_FLAG_HAS_0RTT ? 1:0,
xc->conn_flag & XQC_CONN_FLAG_0RTT_OK ? 1:0,
xc->conn_type == XQC_CONN_TYPE_SERVER ? (xc->conn_flag & XQC_CONN_FLAG_TOKEN_OK ? 1:0) : (-1),
(xc->handshake_complete_time > xc->conn_create_time) ? (xc->handshake_complete_time - xc->conn_create_time) : 0,
(xc->first_data_send_time > xc->conn_create_time) ? (xc->first_data_send_time - xc->conn_create_time) : 0,
xqc_monotonic_timestamp() - xc->conn_create_time, xc->key_update_ctx.key_update_cnt,
xc->conn_err, xc->conn_close_msg ? xc->conn_close_msg : "", xqc_conn_addr_str(xc),
xqc_calc_delay(xc->conn_hsk_recv_time, xc->conn_create_time),
xqc_calc_delay(xc->conn_close_recv_time, xc->conn_create_time),
xqc_calc_delay(xc->conn_close_send_time, xc->conn_create_time),
xqc_calc_delay(xc->conn_last_recv_time, xc->conn_create_time),
xqc_calc_delay(xc->conn_last_send_time, xc->conn_create_time),
xc->enable_multipath, xc->create_path_count, xc->validated_path_count, xc->active_path_count,
conn_stats.conn_info, out_alpn_len, out_alpn, conn_stats.total_rebind_count,
conn_stats.total_rebind_valid,
conn_stats.lost_count, conn_stats.tlp_count,
conn_stats.send_count, conn_stats.spurious_loss_count, xc->detected_loss_cnt,
xc->max_pto_cnt, xc->finished_streams, xc->cli_bidi_streams, xc->svr_bidi_streams,
fec_flag,
ensch_str, desch_str, xc->fec_neg_fail_reason,
fec_mpm_str, conn_stats.send_fec_cnt, xc->fec_ctl ? xc->fec_ctl->fec_recover_pkt_cnt : 0,
xc->pkt_out_size, xc->max_pkt_out_size, xc->probing_pkt_out_size,
conn_stats.extern_conn_info, xc->max_acked_po_size,
xc->local_settings.enable_pmtud & xc->remote_settings.enable_pmtud, xc->conn_avg_close_delay
);
xqc_log_event(xc->log, CON_CONNECTION_CLOSED, xc);
xqc_engine_remove_wakeup_queue(xc->engine, xc);
xqc_list_head_t *pos, *next;
xqc_stream_t *stream;
xqc_packet_in_t *packet_in;
/* destroy streams, must before conn_close_notify */
xqc_list_for_each_safe(pos, next, &xc->conn_all_streams) {
stream = xqc_list_entry(pos, xqc_stream_t, all_stream_list);
XQC_STREAM_CLOSE_MSG(stream, "conn closed");
xqc_destroy_stream(stream);
}
xqc_conn_destroy_0rtt_datagram_buffer_list(xc);
if (xc->conn_settings.datagram_redundant_probe
&& xc->dgram_probe_timer >= 0) {
xqc_conn_unregister_gp_timer(xc, xc->dgram_probe_timer);
}
if (xc->last_dgram) {
xqc_var_buf_free(xc->last_dgram);
xc->last_dgram = NULL;
}
/* notify destruction */
if (xc->conn_flag & XQC_CONN_FLAG_UPPER_CONN_EXIST) {
/* ALPN negotiated, notify close through application layer protocol callback function */
if (xc->app_proto_cbs.conn_cbs.conn_close_notify) {
xc->app_proto_cbs.conn_cbs.conn_close_notify(xc, &xc->scid_set.user_scid,
xc->user_data,
xc->proto_data);
} else if (xc->transport_cbs.server_refuse) {
/* ALPN context is not initialized, ClientHello has not been received */
xc->transport_cbs.server_refuse(xc->engine, xc, &xc->scid_set.user_scid, xc->user_data);
xqc_log(xc->log, XQC_LOG_REPORT,
"|conn close notified by refuse|%s", xqc_conn_addr_str(xc));
} else {
xqc_log(xc->log, XQC_LOG_REPORT,
"|conn close event not notified|%s", xqc_conn_addr_str(xc));
}
xc->conn_flag &= ~XQC_CONN_FLAG_UPPER_CONN_EXIST;
}
/* destroy gp_timer list */
xqc_timer_destroy_gp_timer_list(&xc->conn_timer_manager);
xqc_send_queue_destroy(xc->conn_send_queue);
#ifdef XQC_ENABLE_FEC
if (xc->fec_ctl) {
xqc_fec_ctl_destroy(xc->fec_ctl);
}
#endif
/* free streams hash */
if (xc->streams_hash) {
xqc_id_hash_release(xc->streams_hash);
xc->streams_hash = NULL;
}
if (xc->passive_streams_hash) {
xqc_id_hash_release(xc->passive_streams_hash);
xc->passive_streams_hash = NULL;
}
xqc_conn_destroy_paths_list(xc);
for (xqc_encrypt_level_t encrypt_level = XQC_ENC_LEV_INIT; encrypt_level < XQC_ENC_LEV_MAX; encrypt_level++) {
xqc_list_for_each_safe(pos, next, &xc->undecrypt_packet_in[encrypt_level]) {
packet_in = xqc_list_entry(pos, xqc_packet_in_t, pi_list);
xqc_list_del_init(pos);
xqc_packet_in_destroy(packet_in, xc);
}
}
for (xqc_encrypt_level_t encrypt_level = XQC_ENC_LEV_INIT;
encrypt_level < XQC_ENC_LEV_MAX; encrypt_level++)
{
if (xc->crypto_stream[encrypt_level]) {
xqc_destroy_crypto_stream(xc, xc->crypto_stream[encrypt_level]);
}
}
xqc_conn_destroy_ping_notification_list(xc);
/* remove from engine's conns_hash and destroy cid_set*/
xqc_conn_destroy_cids(xc);
if (xc->tls) {
xqc_tls_destroy(xc->tls);
}
if (xc->enc_pkt) {
xqc_free(xc->enc_pkt);
}
xqc_log_release(xc->log);
if (xc->alpn) {
xqc_free(xc->alpn);
}
/* free pool, must be the last thing to do */
if (xc->conn_pool) {
xqc_destroy_pool(xc->conn_pool);
}
}
void
xqc_conn_set_transport_user_data(xqc_connection_t *conn, void *user_data)
{
conn->user_data = user_data;
}
void
xqc_conn_set_alp_user_data(xqc_connection_t *conn, void *user_data)
{
conn->proto_data = user_data;
}
xqc_int_t
xqc_conn_get_peer_addr(xqc_connection_t *conn, struct sockaddr *addr, socklen_t addr_cap,
socklen_t *peer_addr_len)
{
if (conn->peer_addrlen > addr_cap) {
return -XQC_ENOBUF;
}
*peer_addr_len = conn->peer_addrlen;
xqc_memcpy(addr, conn->peer_addr, conn->peer_addrlen);
return XQC_OK;
}
xqc_int_t
xqc_conn_get_local_addr(xqc_connection_t *conn, struct sockaddr *addr, socklen_t addr_cap,
socklen_t *local_addr_len)
{
if (conn->local_addrlen > addr_cap) {
return -XQC_ENOBUF;
}
*local_addr_len = conn->local_addrlen;
xqc_memcpy(addr, conn->local_addr, conn->local_addrlen);
return XQC_OK;
}
void
xqc_conn_encode_transport_state(xqc_connection_t *conn, char *buf, size_t buf_sz)
{
xqc_path_ctx_t *path;
xqc_list_head_t *pos, *next;
xqc_send_ctl_t *send_ctl;
int ret, i;
size_t cursor;
uint64_t cwnd, inflight, sched_queue, pacing_rate, est_bw;
xqc_usec_t srtt_ms;
cursor = 0;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
send_ctl = path->path_send_ctl;
if (path->path_state == XQC_PATH_STATE_ACTIVE) {
//KB
cwnd = send_ctl->ctl_cong_callback->xqc_cong_ctl_get_cwnd(send_ctl->ctl_cong) >> 10;
//KBps
est_bw = xqc_send_ctl_get_est_bw(send_ctl) >> 10;
srtt_ms = send_ctl->ctl_srtt / 1000;
//KB
inflight = send_ctl->ctl_bytes_in_flight >> 10;
//KB
sched_queue = path->path_schedule_bytes >> 10;
//KBps
pacing_rate = xqc_send_ctl_get_pacing_rate(send_ctl) >> 10;
ret = snprintf(buf + cursor, buf_sz - cursor,
"(%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64"),",
path->path_id, cwnd, inflight, sched_queue, est_bw, pacing_rate, srtt_ms);
cursor += ret;
if (cursor >= buf_sz) {
break;
}
}
}
cursor = xqc_min(cursor, buf_sz);
for (i = cursor - 1; i >= 0; i--) {
if (buf[i] == ',') {
buf[i] = '\0';
break;
}
}
buf[buf_sz - 1] = '\0';
}
xqc_int_t
xqc_conn_send_ping_internal(xqc_connection_t *conn, void *ping_user_data, xqc_bool_t notify)
{
xqc_int_t ret;
xqc_path_ctx_t *path;
xqc_list_head_t *pos, *next;
xqc_bool_t has_ping;
xqc_ping_record_t *pr;
ret = XQC_OK;
if (conn->conn_state >= XQC_CONN_STATE_CLOSING) {
return ret;
}
pr = xqc_conn_create_ping_record(conn);
if (pr == NULL) {
return -XQC_EMALLOC;
}
has_ping = XQC_FALSE;
if (conn->enable_multipath && conn->conn_settings.mp_ping_on) {
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
if (path->path_state == XQC_PATH_STATE_ACTIVE) {
ret = xqc_write_ping_to_packet(conn, path, ping_user_data, notify, pr);
if (ret < 0) {
xqc_log(conn->log, XQC_LOG_ERROR, "|write ping error|path:%ui|", path->path_id);
} else {
has_ping = XQC_TRUE;
}
}
}
} else {
ret = xqc_write_ping_to_packet(conn, NULL, ping_user_data, notify, pr);
if (ret < 0) {
xqc_log(conn->log, XQC_LOG_ERROR, "|write ping error|");
} else {
has_ping = XQC_TRUE;
}
}
if (!has_ping) {
xqc_conn_destroy_ping_record(pr);
return ret;
}
return XQC_OK;
}
/* used by upper level, shall never be invoked in xquic */
xqc_int_t
xqc_conn_send_ping(xqc_engine_t *engine, const xqc_cid_t *cid, void *ping_user_data)
{
xqc_connection_t *conn;
xqc_int_t ret;
conn = xqc_engine_conns_hash_find(engine, cid, 's');
if (!conn) {
xqc_log(engine->log, XQC_LOG_ERROR, "|can not find connection|cid:%s",
xqc_scid_str(engine, cid));
return -XQC_ECONN_NFOUND;
}
ret = xqc_conn_send_ping_internal(conn, ping_user_data, XQC_TRUE);
if (ret) {
return ret;
}
xqc_engine_remove_wakeup_queue(engine, conn);
xqc_engine_add_active_queue(engine, conn);
xqc_engine_conn_logic(engine, conn);
return XQC_OK;
}
/* check whether conn should close when write_socket return XQC_SOCKET_ERROR */
xqc_bool_t
xqc_conn_should_close(xqc_connection_t *conn, xqc_path_ctx_t *path)
{
if (!conn->enable_multipath) {
return XQC_TRUE;
}
if (conn->active_path_count < 2 && path->path_state == XQC_PATH_STATE_ACTIVE) {
return XQC_TRUE;
}
xqc_int_t ret;
if (path->path_state < XQC_PATH_STATE_CLOSING) {
ret = xqc_path_immediate_close(path);
if (ret != XQC_OK) {
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_path_immediate_close error|ret:%d|", ret);
}
}
return XQC_FALSE;
}
void
xqc_conn_try_to_update_mss(xqc_connection_t *conn)
{
xqc_path_ctx_t *path;
xqc_list_head_t *pos, *next;
size_t min_pkt_out_size = 0;
size_t max_pkt_out_size = 0;
/* try to update conn MTU */
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
if (path->path_state >= XQC_PATH_STATE_CLOSING) {
continue;
}
if (min_pkt_out_size == 0 || path->curr_pkt_out_size < min_pkt_out_size) {
min_pkt_out_size = path->curr_pkt_out_size;
max_pkt_out_size = path->path_max_pkt_out_size;
}
}
if (min_pkt_out_size > conn->pkt_out_size) {
conn->pkt_out_size = min_pkt_out_size;
/* try to update PMTUD probing info */
conn->max_pkt_out_size = max_pkt_out_size;
conn->probing_pkt_out_size = max_pkt_out_size;
conn->probing_cnt = 0;
/* launch new probing immediately */
conn->conn_flag |= XQC_CONN_FLAG_PMTUD_PROBING;
xqc_timer_unset(&conn->conn_timer_manager, XQC_TIMER_PMTUD_PROBING);
/* update datagram mss */
xqc_datagram_record_mss(conn);
}
}
ssize_t
xqc_send_burst(xqc_connection_t *conn, xqc_path_ctx_t *path, struct iovec *iov, int cnt)
{
ssize_t sent_size = 0;
int sent_cnt = 0;
if (conn->pkt_filter_cb) {
for (sent_cnt = 0; sent_cnt < cnt; sent_cnt++) {
sent_size = conn->pkt_filter_cb(iov[sent_cnt].iov_base,
iov[sent_cnt].iov_len,
(struct sockaddr *)conn->peer_addr,
conn->peer_addrlen,
conn->pkt_filter_cb_user_data);
if (sent_size < 0) {
xqc_log(conn->log, XQC_LOG_ERROR,
"|pkt_filter_cb error|conn:%p|"
"size:%ud|sent:%z|sent_cnt:%d|",
conn, iov[sent_cnt].iov_len, sent_size,
sent_cnt);
sent_size = sent_size == XQC_SOCKET_EAGAIN ? -XQC_EAGAIN : -XQC_ESOCKET;
break;
}
}
if (sent_size == -XQC_EAGAIN && sent_cnt == 0) {
sent_cnt = -XQC_EAGAIN;
} else if (sent_size == -XQC_ESOCKET) {
sent_cnt = -XQC_EPACKET_FILETER_CALLBACK;
}
} else if (conn->transport_cbs.write_mmsg_ex) {
sent_cnt = conn->transport_cbs.write_mmsg_ex(path->path_id, iov, cnt,
(struct sockaddr *)path->peer_addr,
path->peer_addrlen,
xqc_conn_get_user_data(conn));
if (sent_cnt < 0) {
xqc_log(conn->log, XQC_LOG_ERROR, "|error send mmsg|");
if (sent_cnt == XQC_SOCKET_ERROR) {
path->path_flag |= XQC_PATH_FLAG_SOCKET_ERROR;
if (xqc_conn_should_close(conn, path)) {
xqc_log(conn->log, XQC_LOG_ERROR, "|socket exception, close connection|");
conn->conn_state = XQC_CONN_STATE_CLOSED;
xqc_log_event(conn->log, CON_CONNECTION_STATE_UPDATED, conn);
}
}
sent_cnt = sent_cnt == XQC_SOCKET_EAGAIN ? -XQC_EAGAIN : -XQC_ESOCKET;
}
} else {
sent_cnt = conn->transport_cbs.write_mmsg(iov, cnt,
(struct sockaddr *)conn->peer_addr,
conn->peer_addrlen,
xqc_conn_get_user_data(conn));
if (sent_cnt < 0) {
xqc_log(conn->log, XQC_LOG_ERROR, "|error send mmsg|");
if (sent_cnt == XQC_SOCKET_ERROR) {
xqc_log(conn->log, XQC_LOG_ERROR, "|socket exception, close connection|");
conn->conn_state = XQC_CONN_STATE_CLOSED;
xqc_log_event(conn->log, CON_CONNECTION_STATE_UPDATED, conn);
}
sent_cnt = sent_cnt == XQC_SOCKET_EAGAIN ? -XQC_EAGAIN : -XQC_ESOCKET;
}
}
return sent_cnt;
}
xqc_int_t
xqc_check_acked_or_dropped_pkt(xqc_connection_t *conn,
xqc_packet_out_t *packet_out, xqc_send_type_t send_type)
{
if (xqc_send_ctl_indirectly_ack_or_drop_po(conn, packet_out)) {
return XQC_TRUE;
}
if (send_type == XQC_SEND_TYPE_RETRANS) {
/* If not a TLP packet, mark it LOST */
packet_out->po_flag |= XQC_POF_LOST;
}
xqc_log(conn->log, XQC_LOG_DEBUG,
"|conn:%p|pkt_num:%ui|size:%ud|pkt_type:%s|frame:%s|",
conn, packet_out->po_pkt.pkt_num, packet_out->po_used_size,
xqc_pkt_type_2_str(packet_out->po_pkt.pkt_type),
xqc_frame_type_2_str(conn->engine, packet_out->po_frame_types));
return XQC_FALSE;
}
void
xqc_conn_schedule_packets(xqc_connection_t *conn, xqc_list_head_t *head,
xqc_bool_t packets_are_limited_by_cc, xqc_send_type_t send_type)
{
ssize_t ret;
uint32_t send_repair_num, src_syb_num;
uint64_t stream_id;
xqc_bool_t cc_blocked, reset_rpr_timer;
xqc_usec_t now, cq_fin_timeout;
xqc_path_ctx_t *path;
xqc_list_head_t *pos, *next;
xqc_packet_out_t *packet_out;
xqc_stream_t *stream;
now = xqc_monotonic_timestamp();
reset_rpr_timer = 0;
xqc_list_for_each_safe(pos, next, head) {
packet_out = xqc_list_entry(pos, xqc_packet_out_t, po_list);
/* 1. 已设置特定路径发送的包,例如:PATH_CHALLENGE PATH_RESPONSE MP_ACK(原路径ACK) */
if (xqc_packet_out_on_specific_path(conn, packet_out, &path)) {
if (path == NULL) {
continue;
}
xqc_log(conn->log, XQC_LOG_DEBUG, "|specify|path:%ui|state:%d|frame_type:%s|stream_id:%ui|stream_offset:%ui|",
path->path_id, path->path_state, xqc_frame_type_2_str(conn->engine, packet_out->po_frame_types),
packet_out->po_stream_id, packet_out->po_stream_offset);
/* 2. schedule packet multipath */
} else {
path = conn->scheduler_callback->
xqc_scheduler_get_path(conn->scheduler,
conn, packet_out,
packets_are_limited_by_cc,
0, &cc_blocked);
if (path == NULL) {
if (cc_blocked) {
conn->sched_cc_blocked++;
if (packet_out->po_sched_cwnd_blk_ts == 0) {
packet_out->po_sched_cwnd_blk_ts = now;
}
}
if (xqc_timer_is_set(&conn->conn_timer_manager, XQC_TIMER_QUEUE_FIN)) {
reset_rpr_timer = 1;
}
break;
}
}
#ifdef XQC_ENABLE_FEC
/* FEC encode packets */
if (conn->conn_settings.enable_encode_fec
&& conn->conn_settings.fec_params.fec_encoder_scheme
&& !(packet_out->po_frame_types & XQC_FRAME_BIT_SID
|| packet_out->po_frame_types & XQC_FRAME_BIT_REPAIR_SYMBOL))
{
if (xqc_is_packet_fec_protected(conn, packet_out) == XQC_OK) {
xqc_process_fec_protected_packet(conn, packet_out);
/* if insert repair packet after current po, update next pointer; */
next = pos->next;
reset_rpr_timer = 1;
}
}
#endif
xqc_path_send_buffer_append(path, packet_out, &path->path_schedule_buf[send_type]);
}
if (conn->conn_settings.fec_params.fec_encoder_scheme == XQC_PACKET_MASK_CODE
&& send_type == XQC_SEND_TYPE_NORMAL
&& reset_rpr_timer)
{
cq_fin_timeout = xqc_conn_get_queue_fin_timeout(conn);
if (cq_fin_timeout) {
xqc_timer_set(&conn->conn_timer_manager, XQC_TIMER_QUEUE_FIN, now, cq_fin_timeout * 1000);
}
}
}
static inline void
xqc_conn_log_sent_packet(xqc_connection_t *c, xqc_packet_out_t *po,
xqc_usec_t timestamp)
{
int index = c->snd_pkt_stats.curr_index;
c->snd_pkt_stats.pkt_frames[index] = po->po_frame_types;
c->snd_pkt_stats.pkt_size[index] = po->po_used_size;
c->snd_pkt_stats.pkt_timestamp[index] = xqc_calc_delay(timestamp,
c->conn_create_time);
c->snd_pkt_stats.pkt_timestamp[index] /= 1000;
c->snd_pkt_stats.pkt_types[index] = po->po_pkt.pkt_type;
c->snd_pkt_stats.pkt_pn[index] = po->po_pkt.pkt_num;
c->snd_pkt_stats.conn_sent_pkts++;
c->snd_pkt_stats.curr_index = (index + 1) % 3;
}
void
xqc_on_packets_send_burst(xqc_connection_t *conn, xqc_path_ctx_t *path, ssize_t sent, xqc_usec_t now, xqc_send_type_t send_type)
{
xqc_list_head_t *pos, *next;
xqc_packet_out_t *packet_out;
int remove_count = 0; /* remove from send */
xqc_send_ctl_t *send_ctl = path->path_send_ctl;
xqc_pn_ctl_t *pn_ctl = xqc_get_pn_ctl(conn, path);
xqc_send_queue_t *send_queue = conn->conn_send_queue;
xqc_list_for_each_safe(pos, next, &path->path_schedule_buf[send_type]) {
if (remove_count >= sent) {
break;
}
packet_out = xqc_list_entry(pos, xqc_packet_out_t, po_list);
xqc_conn_log_sent_packet(conn, packet_out, now);
if (xqc_has_packet_number(&packet_out->po_pkt)) {
/* count packets with pkt_num in the send control */
if (XQC_CAN_IN_FLIGHT(packet_out->po_frame_types
&& xqc_pacing_is_on(&send_ctl->ctl_pacing)))
{
xqc_pacing_on_packet_sent(&send_ctl->ctl_pacing, packet_out->po_used_size);
}
xqc_send_ctl_on_packet_sent(send_ctl, pn_ctl, packet_out, now);
xqc_path_send_buffer_remove(path, packet_out);
if (XQC_IS_ACK_ELICITING(packet_out->po_frame_types)) {
xqc_send_queue_insert_unacked(packet_out,
&send_queue->sndq_unacked_packets[packet_out->po_pkt.pkt_pns],
send_queue);
} else {
xqc_send_queue_insert_free(packet_out, &send_queue->sndq_free_packets, send_queue);
}
xqc_log(conn->log, XQC_LOG_INFO,
"|<==|conn:%p|path:%ui|pkt_num:%ui|size:%ud|sent:%z|pkt_type:%s|frame:%s|inflight:%ud|now:%ui|",
conn, path->path_id, packet_out->po_pkt.pkt_num, packet_out->po_used_size, sent,
xqc_pkt_type_2_str(packet_out->po_pkt.pkt_type),
xqc_frame_type_2_str(conn->engine, packet_out->po_frame_types),
send_ctl->ctl_bytes_in_flight, now);
} else {
/* packets with no packet number can't be acknowledged, hence they need no control */
xqc_path_send_buffer_remove(path, packet_out);
xqc_send_queue_insert_free(packet_out, &send_queue->sndq_free_packets, send_queue);
xqc_log(conn->log, XQC_LOG_INFO, "|<==|conn:%p|size:%ud|sent:%z|pkt_type:%s|",
conn, packet_out->po_used_size, sent, xqc_pkt_type_2_str(packet_out->po_pkt.pkt_type));
}
remove_count++;
}
}
void
xqc_convert_pkt_0rtt_2_1rtt(xqc_connection_t *conn, xqc_packet_out_t *packet_out)
{
/* long header to short header, directly write old buffer */
unsigned int ori_po_used_size = packet_out->po_used_size;
unsigned char *ori_payload = packet_out->po_payload;
unsigned int ori_hdr_len = packet_out->po_payload - packet_out->po_buf;
unsigned int ori_payload_len = ori_po_used_size - ori_hdr_len;
/* convert pkt info */
packet_out->po_pkt.pkt_pns = XQC_PNS_APP_DATA;
packet_out->po_pkt.pkt_type = XQC_PTYPE_SHORT_HEADER;
/* copy header */
packet_out->po_used_size = 0;
int ret = xqc_gen_short_packet_header(packet_out, conn->dcid_set.current_dcid.cid_buf,
conn->dcid_set.current_dcid.cid_len, XQC_PKTNO_BITS, 0,
conn->key_update_ctx.cur_out_key_phase);
packet_out->po_used_size = ret;
if (ori_hdr_len < ret) {
xqc_log(conn->log, XQC_LOG_ERROR, "|fatal|long_header_is_shorter_than_short_header|");
XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR);
return;
}
unsigned int hdr_offset_diff = (ori_hdr_len - ret);
/* copy frame directly */
memmove(packet_out->po_buf + ret, ori_payload, ori_payload_len);
packet_out->po_payload = packet_out->po_buf + ret;
packet_out->po_used_size += ori_payload_len;
if (packet_out->po_ack_offset > 0) {
if (packet_out->po_ack_offset < hdr_offset_diff) {
xqc_log(conn->log, XQC_LOG_ERROR, "|fatal|wrong_ack_frame_offset|");
XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR);
return;
}
packet_out->po_ack_offset -= hdr_offset_diff;
}
xqc_log(conn->log, XQC_LOG_DEBUG, "|0RTT to 1RTT|conn:%p|type:%d|pkt_num:%ui|pns:%d|frame:%s|",
conn, packet_out->po_pkt.pkt_type, packet_out->po_pkt.pkt_num, packet_out->po_pkt.pkt_pns,
xqc_frame_type_2_str(conn->engine, packet_out->po_frame_types));
}
ssize_t
xqc_path_send_burst_packets(xqc_connection_t *conn, xqc_path_ctx_t *path,
int congest, xqc_send_type_t send_type)
{
ssize_t ret;
struct iovec iov_array[XQC_MAX_SEND_MSG_ONCE];
char enc_pkt_array[XQC_MAX_SEND_MSG_ONCE][XQC_CONN_MAX_UDP_PAYLOAD_SIZE];
int burst_cnt = 0;
xqc_packet_out_t *packet_out;
xqc_list_head_t *pos, *next;
xqc_send_ctl_t *send_ctl = path->path_send_ctl;
uint32_t total_bytes_to_send = 0;
/* process packets */
xqc_usec_t now = xqc_monotonic_timestamp();
xqc_list_for_each_safe(pos, next, &path->path_schedule_buf[send_type]) {
/* process one packet */
packet_out = xqc_list_entry(pos, xqc_packet_out_t, po_list);
iov_array[burst_cnt].iov_base = enc_pkt_array[burst_cnt];
iov_array[burst_cnt].iov_len = XQC_CONN_MAX_UDP_PAYLOAD_SIZE;
if (xqc_has_packet_number(&packet_out->po_pkt)) {
if (xqc_check_acked_or_dropped_pkt(conn, packet_out, send_type)) {
continue;
}
/* check the anti-amplification limit, will allow a bit larger than 3x received */
if (xqc_send_ctl_check_anti_amplification(send_ctl, total_bytes_to_send)) {
xqc_log(conn->log, XQC_LOG_INFO,
"|blocked by anti amplification limit|total_sent:%ui|3*total_recv:%ui|",
send_ctl->ctl_bytes_send + total_bytes_to_send, 3 * send_ctl->ctl_bytes_recv);
break;
}
/* check cc limit */
if (congest
&& !xqc_send_packet_check_cc(send_ctl, packet_out, total_bytes_to_send, now))
{
send_ctl->ctl_conn->send_cc_blocked++;
break;
}
/* retransmit 0-RTT packets in 1-RTT if 1-RTT keys are ready. */
if (XQC_UNLIKELY(packet_out->po_pkt.pkt_type == XQC_PTYPE_0RTT
&& conn->conn_flag & XQC_CONN_FLAG_CAN_SEND_1RTT))
{
xqc_convert_pkt_0rtt_2_1rtt(conn, packet_out);
}
/* enc packet */
ret = xqc_conn_enc_packet(conn, path, packet_out, iov_array[burst_cnt].iov_base,
XQC_CONN_MAX_UDP_PAYLOAD_SIZE, &iov_array[burst_cnt].iov_len, now);
if (XQC_OK != ret) {
return ret;
}
total_bytes_to_send += packet_out->po_used_size;
} else {
xqc_memcpy(iov_array[burst_cnt].iov_base, packet_out->po_buf, packet_out->po_used_size);
iov_array[burst_cnt].iov_len = packet_out->po_used_size;
}
/* reach send limit, break and send packets */
burst_cnt++;
if (burst_cnt >= XQC_MAX_SEND_MSG_ONCE) {
burst_cnt = XQC_MAX_SEND_MSG_ONCE;
break;
}
}
/* nothing to send, return */
if (burst_cnt == 0) {
return burst_cnt;
}
/* burst send packets */
ret = xqc_send_burst(conn, path, iov_array, burst_cnt);
if (ret < 0) {
return ret;
} else if (ret != burst_cnt) {
xqc_log(conn->log, XQC_LOG_ERROR, "|error send msg|sent:%ui||cnt:%d|", ret, burst_cnt);
}
xqc_on_packets_send_burst(conn, path, ret, now, send_type);
return ret;
}
void
xqc_path_send_packets_batch(xqc_connection_t *conn, xqc_path_ctx_t *path,
xqc_list_head_t *head, int congest, xqc_send_type_t send_type)
{
ssize_t send_burst_count = 0;
while (!(xqc_list_empty(&path->path_schedule_buf[send_type]))) {
send_burst_count = xqc_path_send_burst_packets(conn, path, congest, send_type);
if (send_burst_count != XQC_MAX_SEND_MSG_ONCE) {
break;
}
}
/* @FIXME: in the case of EAGAIN, we should not reschedule packets. */
if (send_burst_count < 0 && send_burst_count != -XQC_EAGAIN) {
xqc_path_send_buffer_clear(conn, path, head, send_type);
}
}
void
xqc_conn_send_packets_batch(xqc_connection_t *conn)
{
int congest;
xqc_path_ctx_t *path;
xqc_list_head_t *head;
xqc_list_head_t *pos, *next;
congest = 0;
head = &conn->conn_send_queue->sndq_send_packets_high_pri;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
xqc_path_send_packets_batch(conn, path, head, congest, XQC_SEND_TYPE_NORMAL_HIGH_PRI);
}
head = &conn->conn_send_queue->sndq_send_packets;
congest = 1;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
xqc_path_send_packets_batch(conn, path, head, congest, XQC_SEND_TYPE_NORMAL);
}
return;
}
void
xqc_path_send_packets(xqc_connection_t *conn, xqc_path_ctx_t *path,
xqc_list_head_t *head, int congest, xqc_send_type_t send_type)
{
ssize_t ret = 0;
xqc_list_head_t *pos, *next;
xqc_packet_out_t *packet_out;
xqc_send_ctl_t *send_ctl = path->path_send_ctl;
xqc_send_queue_t *send_queue = conn->conn_send_queue;
xqc_usec_t now = xqc_monotonic_timestamp();
xqc_list_for_each_safe(pos, next, &path->path_schedule_buf[send_type]) {
packet_out = xqc_list_entry(pos, xqc_packet_out_t, po_list);
if (xqc_check_acked_or_dropped_pkt(conn, packet_out, send_type)) {
xqc_log(conn->log, XQC_LOG_DEBUG, "|path:%ui|canceled_bytes:%ud|reinj:%d|", path->path_id, packet_out->po_used_size, XQC_MP_PKT_REINJECTED(packet_out));
continue;
}
/* check the anti-amplification limit, will allow a bit larger than 3x received */
if (xqc_send_ctl_check_anti_amplification(send_ctl, 0)) {
xqc_log(conn->log, XQC_LOG_INFO,
"|blocked by anti amplification limit|total_sent:%ui|3*total_recv:%ui|",
send_ctl->ctl_bytes_send, 3 * send_ctl->ctl_bytes_recv);
break;
}
/* check cc limit */
if (congest
&& !xqc_send_packet_check_cc(send_ctl, packet_out, 0, now))
{
send_ctl->ctl_conn->send_cc_blocked++;
break;
}
ret = xqc_path_send_one_packet(conn, path, packet_out);
if (ret < 0) {
break;
}
if (XQC_CAN_IN_FLIGHT(packet_out->po_frame_types)
&& xqc_pacing_is_on(&send_ctl->ctl_pacing))
{
xqc_pacing_on_packet_sent(&send_ctl->ctl_pacing, packet_out->po_used_size);
}
if (packet_out->po_frame_types & XQC_FRAME_BIT_DATAGRAM) {
xqc_log(conn->log, XQC_LOG_DEBUG, "|dgram_id:%ui|", packet_out->po_dgram_id);
}
/* move send list to unacked list */
xqc_path_send_buffer_remove(path, packet_out);
if (XQC_IS_ACK_ELICITING(packet_out->po_frame_types)) {
xqc_send_queue_insert_unacked(packet_out,
&send_queue->sndq_unacked_packets[packet_out->po_pkt.pkt_pns],
send_queue);
} else {
xqc_send_queue_insert_free(packet_out, &send_queue->sndq_free_packets, send_queue);
}
}
/* @FIXME: in the case of EAGAIN, we should not reschedule packets. */
if (ret < 0 && ret != -XQC_EAGAIN) {
xqc_path_send_buffer_clear(conn, path, head, send_type);
}
}
void
xqc_conn_send_packets(xqc_connection_t *conn)
{
XQC_DEBUG_PRINT
int congest;
xqc_path_ctx_t *path;
xqc_list_head_t *head;
xqc_list_head_t *pos, *next;
/* high priority packets are not limited by CC */
congest = 0;
head = &conn->conn_send_queue->sndq_send_packets_high_pri;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
xqc_path_send_packets(conn, path, head, congest, XQC_SEND_TYPE_NORMAL_HIGH_PRI);
}
congest = 1;
head = &conn->conn_send_queue->sndq_send_packets;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
xqc_path_send_packets(conn, path, head, congest, XQC_SEND_TYPE_NORMAL);
}
}
xqc_int_t
xqc_need_padding(xqc_connection_t *conn, xqc_packet_out_t *packet_out)
{
xqc_int_t ret = XQC_FALSE;
if (packet_out->po_pkt.pkt_pns == XQC_PNS_INIT) {
if (conn->engine->eng_type == XQC_ENGINE_CLIENT) {
/*
* client MUST expand the payload of all UDP datagrams carrying
* Initial packets to at least the smallest allowed maximum datagram
* size of 1200 bytes
*/
ret = XQC_TRUE;
} else {
/*
* server MUST expand the payload of all UDP datagrams carrying ack-
* eliciting Initial packets to at least the smallest allowed maximum
* datagram size of 1200 bytes
*/
if (XQC_IS_ACK_ELICITING(packet_out->po_frame_types)) {
ret = XQC_TRUE;
}
}
} else if ((packet_out->po_frame_types & XQC_FRAME_BIT_PATH_CHALLENGE)
|| (packet_out->po_frame_types & XQC_FRAME_BIT_PATH_RESPONSE)
|| (packet_out->po_flag & XQC_POF_PMTUD_PROBING))
{
return XQC_TRUE;
}
return ret;
}
xqc_int_t
xqc_conn_enc_packet(xqc_connection_t *conn,
xqc_path_ctx_t *path, xqc_packet_out_t *packet_out,
char *enc_pkt, size_t enc_pkt_cap, size_t *enc_pkt_len, xqc_usec_t current_time)
{
/* update dcid by send path */
xqc_short_packet_update_dcid(packet_out, path->path_dcid);
/* pad packet if needed */
if (xqc_need_padding(conn, packet_out)) {
xqc_gen_padding_frame(conn, packet_out);
}
/* generate packet number and update packet length, might do packet number encoding here */
xqc_pn_ctl_t *pn_ctl = xqc_get_pn_ctl(conn, path);
xqc_send_ctl_set_next_pn_for_packet(conn, pn_ctl, packet_out, current_time);
xqc_write_packet_number(packet_out->po_ppktno, packet_out->po_pkt.pkt_num, XQC_PKTNO_BITS);
xqc_long_packet_update_length(packet_out);
xqc_short_packet_update_key_phase(packet_out, conn->key_update_ctx.cur_out_key_phase);
if (conn->conn_settings.marking_reinjection) {
xqc_packet_update_reserved_bits(packet_out);
}
/* encrypt */
xqc_int_t ret = xqc_packet_encrypt_buf(conn, packet_out, enc_pkt, enc_pkt_cap, enc_pkt_len);
if (ret < 0) {
xqc_log(conn->log, XQC_LOG_ERROR, "|encrypt packet error|");
conn->conn_state = XQC_CONN_STATE_CLOSED;
xqc_log_event(conn->log, CON_CONNECTION_STATE_UPDATED, conn);
return -XQC_EENCRYPT;
}
packet_out->po_sent_time = current_time;
return XQC_OK;
}
/* send data with callback, and process callback errors */
ssize_t
xqc_send(xqc_connection_t *conn, xqc_path_ctx_t *path, unsigned char *data, unsigned int len)
{
ssize_t sent;
if (conn->pkt_filter_cb) {
sent = conn->pkt_filter_cb(data, len, (struct sockaddr *)conn->peer_addr,
conn->peer_addrlen, conn->pkt_filter_cb_user_data);
if (sent < 0) {
xqc_log(conn->log, XQC_LOG_ERROR, "|pkt_filter_cb error|conn:%p|"
"size:%ud|sent:%z|", conn, len, sent);
return sent == XQC_SOCKET_EAGAIN ? -XQC_EAGAIN : -XQC_EPACKET_FILETER_CALLBACK;
}
sent = len;
} else if (conn->transport_cbs.write_socket_ex) {
sent = conn->transport_cbs.write_socket_ex(path->path_id, data, len,
(struct sockaddr *)path->peer_addr,
path->peer_addrlen,
xqc_conn_get_user_data(conn));
if (sent != len) {
xqc_log(conn->log, XQC_LOG_ERROR,
"|write_socket error|conn:%p|size:%ud|sent:%z|", conn, len, sent);
xqc_log(conn->log, XQC_LOG_ERROR,
"|write_socket error|path:%ui|path_addr:%s|peer_addrlen:%d|",
path->path_id, xqc_path_addr_str(path), (int)path->peer_addrlen);
/* if callback return XQC_SOCKET_ERROR, close the connection */
if (sent == XQC_SOCKET_ERROR) {
path->path_flag |= XQC_PATH_FLAG_SOCKET_ERROR;
if (xqc_conn_should_close(conn, path)) {
xqc_log(conn->log, XQC_LOG_ERROR, "|conn:%p|socket exception, close connection|", conn);
conn->conn_state = XQC_CONN_STATE_CLOSED;
xqc_log_event(conn->log, CON_CONNECTION_STATE_UPDATED, conn);
}
}
return sent == XQC_SOCKET_EAGAIN ? -XQC_EAGAIN : -XQC_ESOCKET;
}
} else {
sent = conn->transport_cbs.write_socket(data, len,
(struct sockaddr *)conn->peer_addr,
conn->peer_addrlen,
xqc_conn_get_user_data(conn));
if (sent != len) {
xqc_log(conn->log, XQC_LOG_ERROR,
"|write_socket error|conn:%p|size:%ud|sent:%z|", conn, len, sent);
/* if callback return XQC_SOCKET_ERROR, close the connection */
if (sent == XQC_SOCKET_ERROR) {
xqc_log(conn->log, XQC_LOG_ERROR, "|conn:%p|socket exception, close connection|", conn);
conn->conn_state = XQC_CONN_STATE_CLOSED;
xqc_log_event(conn->log, CON_CONNECTION_STATE_UPDATED, conn);
}
return sent == XQC_SOCKET_EAGAIN ? -XQC_EAGAIN : -XQC_ESOCKET;
}
}
xqc_log_event(conn->log, TRA_DATAGRAMS_SENT, sent, path->path_id);
return sent;
}
/* send packets which have no packet number */
ssize_t
xqc_process_packet_without_pn(xqc_connection_t *conn, xqc_path_ctx_t *path, xqc_packet_out_t *packet_out)
{
/* directly send to peer */
ssize_t sent = xqc_send(conn, path, packet_out->po_buf, packet_out->po_used_size);
xqc_log(conn->log, XQC_LOG_INFO, "|<==|conn:%p|size:%ud|sent:%z|pkt_type:%s|",
conn, packet_out->po_used_size, sent, xqc_pkt_type_2_str(packet_out->po_pkt.pkt_type));
xqc_log_event(conn->log, TRA_PACKET_SENT, conn, packet_out, path, 0, sent, 0);
if (sent > 0) {
xqc_conn_log_sent_packet(conn, packet_out, xqc_monotonic_timestamp());
}
return sent;
}
/* send data in packet number space */
ssize_t
xqc_send_packet_with_pn(xqc_connection_t *conn, xqc_path_ctx_t *path, xqc_packet_out_t *packet_out)
{
/* record the send time of packet */
xqc_usec_t now = xqc_monotonic_timestamp();
packet_out->po_sent_time = now;
/* send data */
ssize_t sent = xqc_send(conn, path, conn->enc_pkt, conn->enc_pkt_len);
if (sent != conn->enc_pkt_len) {
xqc_log(conn->log, XQC_LOG_ERROR,
"|write_socket error|conn:%p|path:%ui|pkt_num:%ui|size:%ud|sent:%z|pkt_type:%s|frame:%s|now:%ui|",
conn, path->path_id, packet_out->po_pkt.pkt_num, packet_out->po_used_size, sent,
xqc_pkt_type_2_str(packet_out->po_pkt.pkt_type),
xqc_frame_type_2_str(conn->engine, packet_out->po_frame_types), now);
return sent;
} else {
xqc_log(conn->log, XQC_LOG_INFO,
"|<==|conn:%p|path:%ui|pkt_num:%ui|size:%ud|sent:%z|pkt_type:%s|frame:%s|inflight:%ud|now:%ui|stream_id:%ui|stream_offset:%ui|",
conn, path->path_id, packet_out->po_pkt.pkt_num, packet_out->po_used_size, sent,
xqc_pkt_type_2_str(packet_out->po_pkt.pkt_type),
xqc_frame_type_2_str(conn->engine, packet_out->po_frame_types), path->path_send_ctl->ctl_bytes_in_flight, now, packet_out->po_stream_id, packet_out->po_stream_offset);
xqc_log_event(conn->log, TRA_PACKET_SENT, conn, packet_out, path, now, sent, 1);
}
/* deliver packet to send control */
xqc_pn_ctl_t *pn_ctl = xqc_get_pn_ctl(conn, path);
xqc_conn_log_sent_packet(conn, packet_out, now);
xqc_send_ctl_on_packet_sent(path->path_send_ctl, pn_ctl, packet_out, now);
return sent;
}
ssize_t
xqc_enc_packet_with_pn(xqc_connection_t *conn, xqc_path_ctx_t *path, xqc_packet_out_t *packet_out)
{
/* update dcid by send path */
xqc_short_packet_update_dcid(packet_out, path->path_dcid);
/* pad packet if needed */
if (xqc_need_padding(conn, packet_out)) {
xqc_gen_padding_frame(conn, packet_out);
}
/*
* 0RTT packets might be lost or retransmitted during handshake, once client get 1RTT keys,
* it should retransmit the data with 1RTT packets instead.
*/
if (XQC_UNLIKELY(packet_out->po_pkt.pkt_type == XQC_PTYPE_0RTT
&& conn->conn_flag & XQC_CONN_FLAG_CAN_SEND_1RTT))
{
xqc_convert_pkt_0rtt_2_1rtt(conn, packet_out);
}
/* generate packet number */
xqc_pn_ctl_t *pn_ctl = xqc_get_pn_ctl(conn, path);
xqc_usec_t current_time = xqc_monotonic_timestamp();
xqc_send_ctl_set_next_pn_for_packet(conn, pn_ctl, packet_out, current_time);
xqc_write_packet_number(packet_out->po_ppktno, packet_out->po_pkt.pkt_num, XQC_PKTNO_BITS);
xqc_long_packet_update_length(packet_out);
xqc_short_packet_update_key_phase(packet_out, conn->key_update_ctx.cur_out_key_phase);
if (conn->conn_settings.marking_reinjection) {
xqc_packet_update_reserved_bits(packet_out);
}
/* encrypt packet body */
if (xqc_packet_encrypt(conn, packet_out) < 0) {
xqc_log(conn->log, XQC_LOG_ERROR, "|encrypt packet error|");
conn->conn_state = XQC_CONN_STATE_CLOSED;
xqc_log_event(conn->log, CON_CONNECTION_STATE_UPDATED, conn);
return -XQC_EENCRYPT;
}
return XQC_OK;
}
/* process and send packet which has a packet number */
ssize_t
xqc_process_packet_with_pn(xqc_connection_t *conn, xqc_path_ctx_t *path, xqc_packet_out_t *packet_out)
{
ssize_t ret = xqc_enc_packet_with_pn(conn, path, packet_out);
if (ret != XQC_OK) {
xqc_log(conn->log, XQC_LOG_ERROR,
"|path:%ui|pkt_type:%d|pn:%ui|frames:%ui|size:%ud|",
path->path_id, packet_out->po_pkt.pkt_type,
packet_out->po_pkt.pkt_num, (uint64_t)packet_out->po_frame_types,
packet_out->po_used_size);
return ret;
}
/* send packet in packet number space */
return xqc_send_packet_with_pn(conn, path, packet_out);
}
ssize_t
xqc_path_send_one_packet(xqc_connection_t *conn, xqc_path_ctx_t *path, xqc_packet_out_t *packet_out)
{
if (xqc_has_packet_number(&packet_out->po_pkt)) {
return xqc_process_packet_with_pn(conn, path, packet_out);
} else {
return xqc_process_packet_without_pn(conn, path, packet_out);
}
}
void
xqc_conn_check_path_utilization(xqc_connection_t *conn)
{
if (!conn->enable_multipath) {
return;
}
xqc_list_head_t *pos, *next;
xqc_path_ctx_t *path;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
if (path->path_state != XQC_PATH_STATE_ACTIVE) {
continue;
}
if (!xqc_path_is_full(path)
&& conn->scheduler_callback->xqc_scheduler_handle_path_event)
{
conn->scheduler_callback->xqc_scheduler_handle_path_event(conn->scheduler, path, XQC_SCHED_EVENT_PATH_NOT_FULL, NULL);
}
}
}
static void
xqc_conn_schedule_start(xqc_connection_t *conn)
{
if (!conn->enable_multipath) {
return;
}
if (conn->scheduler_callback->xqc_scheduler_handle_conn_event) {
conn->scheduler_callback->xqc_scheduler_handle_conn_event(conn->scheduler, conn, XQC_SCHED_EVENT_CONN_ROUND_START, NULL);
}
}
static void
xqc_conn_schedule_end(xqc_connection_t *conn)
{
if (!conn->enable_multipath) {
return;
}
if (conn->scheduler_callback->xqc_scheduler_handle_conn_event) {
conn->scheduler_callback->xqc_scheduler_handle_conn_event(conn->scheduler, conn, XQC_SCHED_EVENT_CONN_ROUND_FIN, NULL);
}
}
void
xqc_conn_schedule_packets_to_paths(xqc_connection_t *conn)
{
xqc_conn_schedule_start(conn);
/* do neither CC nor Pacing */
xqc_list_head_t *head = &conn->conn_send_queue->sndq_pto_probe_packets;
xqc_conn_schedule_packets(conn, head, XQC_FALSE, XQC_SEND_TYPE_PTO_PROBE);
head = &conn->conn_send_queue->sndq_lost_packets;
xqc_conn_schedule_packets(conn, head, XQC_TRUE, XQC_SEND_TYPE_RETRANS);
head = &conn->conn_send_queue->sndq_send_packets_high_pri;
xqc_conn_schedule_packets(conn, head, XQC_FALSE,
XQC_SEND_TYPE_NORMAL_HIGH_PRI);
/* try to reinject unacked packets if paths still have cwnd */
if (conn->conn_settings.mp_enable_reinjection & XQC_REINJ_UNACK_BEFORE_SCHED) {
xqc_conn_reinject_unack_packets(conn, XQC_REINJ_UNACK_BEFORE_SCHED);
}
head = &conn->conn_send_queue->sndq_send_packets;
xqc_conn_schedule_packets(conn, head, XQC_TRUE, XQC_SEND_TYPE_NORMAL);
/* all packets are scheduled, we need to check if there are paths not fully utilized */
xqc_conn_check_path_utilization(conn);
xqc_conn_schedule_end(conn);
if (conn->conn_settings.mp_enable_reinjection & XQC_REINJ_UNACK_AFTER_SCHED) {
xqc_conn_reinject_unack_packets(conn, XQC_REINJ_UNACK_AFTER_SCHED);
}
}
void
xqc_conn_transmit_pto_probe_packets(xqc_connection_t *conn)
{
/* do neither CC nor Pacing */
xqc_list_head_t *head = &conn->conn_send_queue->sndq_pto_probe_packets;
int congest = 0;
xqc_path_ctx_t *path;
xqc_list_head_t *pos, *next;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
xqc_path_send_packets(conn, path, head, congest, XQC_SEND_TYPE_PTO_PROBE);
}
}
void
xqc_conn_retransmit_lost_packets(xqc_connection_t *conn)
{
xqc_list_head_t *head = &conn->conn_send_queue->sndq_lost_packets;
int congest = 1;
xqc_path_ctx_t *path;
xqc_list_head_t *pos, *next;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
xqc_path_send_packets(conn, path, head, congest, XQC_SEND_TYPE_RETRANS);
}
}
void
xqc_conn_transmit_pto_probe_packets_batch(xqc_connection_t *conn)
{
xqc_list_head_t *head = &conn->conn_send_queue->sndq_pto_probe_packets;
int congest = 0; /* probe packets MUST NOT be blocked by the congestion controller */
xqc_path_ctx_t *path;
xqc_list_head_t *pos, *next;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
xqc_path_send_packets_batch(conn, path, head, congest, XQC_SEND_TYPE_PTO_PROBE);
}
}
void
xqc_conn_retransmit_lost_packets_batch(xqc_connection_t *conn)
{
xqc_list_head_t *head = &conn->conn_send_queue->sndq_lost_packets;
int congest = 1; /* do congestion control */
xqc_path_ctx_t *path;
xqc_list_head_t *pos, *next;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
xqc_path_send_packets_batch(conn, path, head, congest, XQC_SEND_TYPE_RETRANS);
}
}
static inline xqc_packet_out_t *
xqc_conn_gen_ping(xqc_connection_t *conn, xqc_pkt_num_space_t pns)
{
/* convert pns to ptype */
xqc_pkt_type_t ptype = XQC_PTYPE_NUM;
switch (pns) {
case XQC_PNS_INIT:
ptype = XQC_PTYPE_INIT;
break;
case XQC_PNS_HSK:
ptype = XQC_PTYPE_HSK;
break;
case XQC_PNS_APP_DATA:
ptype = XQC_PTYPE_SHORT_HEADER;
break;
default:
break;
}
/* get pkt, which is inserted into sent list */
xqc_packet_out_t *packet_out = xqc_write_new_packet(conn, ptype);
if (packet_out == NULL) {
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|");
return NULL;
}
/* write PING to pkt */
xqc_int_t ret = xqc_gen_ping_frame(packet_out);
if (ret < 0) {
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_ping_frame error|");
xqc_maybe_recycle_packet_out(packet_out, conn);
return NULL;
}
packet_out->po_user_data = NULL;
packet_out->po_used_size += ret;
return packet_out;
}
xqc_int_t
xqc_path_send_ping_to_probe(xqc_path_ctx_t *path, xqc_pkt_num_space_t pns,
xqc_path_specified_flag_t flag)
{
xqc_connection_t *conn = path->parent_conn;
xqc_packet_out_t *packet_out = xqc_conn_gen_ping(conn, pns);
if (packet_out == NULL) {
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|");
return -XQC_EWRITE_PKT;
}
packet_out->po_path_flag |= flag;
packet_out->po_path_id = path->path_id;
/* put PING into probe list, which is not limited by amplification or congestion-control */
xqc_send_queue_remove_send(&packet_out->po_list);
xqc_send_queue_insert_probe(&packet_out->po_list, &conn->conn_send_queue->sndq_pto_probe_packets);
return XQC_OK;
}
int
xqc_conn_send_probe_pkt(xqc_connection_t *c, xqc_path_ctx_t *path,
xqc_packet_out_t *packet_out)
{
xqc_reinjection_mode_t mode;
int reinject = 0;
mode = c->conn_settings.mp_enable_reinjection & XQC_REINJ_UNACK_BEFORE_SCHED;
xqc_log(c->log, XQC_LOG_DEBUG, "|conn:%p|path:%ui|pkt_num:%ui"
"|size:%ud|pkt_type:%s|frame:%s|conn_state:%s|", c,
packet_out->po_path_id, packet_out->po_pkt.pkt_num,
packet_out->po_used_size,
xqc_pkt_type_2_str(packet_out->po_pkt.pkt_type),
xqc_frame_type_2_str(c->engine, packet_out->po_frame_types),
xqc_conn_state_2_str(c->conn_state));
/* reinjection */
if (c->enable_multipath
&& c->reinj_callback
&& c->reinj_callback->xqc_reinj_ctl_can_reinject
&& c->reinj_callback->xqc_reinj_ctl_can_reinject(
c->reinj_ctl, packet_out, mode))
{
if (xqc_conn_try_reinject_packet(c, packet_out) == XQC_OK) {
xqc_log(c->log, XQC_LOG_DEBUG, "|MP|REINJ|reinject pto packets|"
"pkt_num:%ui|size:%ud|pkt_type:%s|frame:%s|",
packet_out->po_pkt.pkt_num, packet_out->po_used_size,
xqc_pkt_type_2_str(packet_out->po_pkt.pkt_type),
xqc_frame_type_2_str(c->engine, packet_out->po_frame_types));
reinject = 1;
}
}
if (packet_out->po_flag & XQC_POF_IN_FLIGHT) {
c->detected_loss_cnt++;
}
xqc_send_ctl_decrease_inflight(c, packet_out);
xqc_send_queue_copy_to_probe(packet_out, c->conn_send_queue, path);
packet_out->po_flag |= XQC_POF_TLP;
if (packet_out->po_frame_types & XQC_FRAME_BIT_DATAGRAM) {
path->path_send_ctl->ctl_lost_dgram_cnt++;
}
return reinject;
}
void
xqc_path_send_one_or_two_ack_elicit_pkts(xqc_path_ctx_t *path,
xqc_pkt_num_space_t pns)
{
xqc_int_t ret;
xqc_connection_t *c;
xqc_packet_out_t *packet_out;
xqc_packet_out_t *packet_out_last_sent; /* for dup pto pkt */
xqc_packet_out_t *packet_out_later_send; /* for sending HSK_DONE first */
xqc_list_head_t *pos, *next;
xqc_list_head_t *sndq;
xqc_int_t probe_num;
xqc_bool_t send_hsd;
int has_reinjection = 0;
c = path->parent_conn;
sndq = &c->conn_send_queue->sndq_unacked_packets[pns];
/* on PTO xquic will try to send 2 ack-eliciting pkts at most. and server
shall send HANDSHAKE_DONE on PTO as it has not been acknowledged. */
probe_num = XQC_CONN_PTO_PKT_CNT_MAX;
send_hsd = XQC_FALSE;
packet_out_last_sent = NULL;
packet_out_later_send = NULL;
xqc_log(c->log, XQC_LOG_DEBUG, "|send two ack-eliciting pkts"
"|path:%ui|pns:%d|", path->path_id, pns);
/* if server's HANDSHAKE_DONE frame was sent and has not been acked, try to
send it */
if ((c->conn_type == XQC_CONN_TYPE_SERVER)
&& !(c->conn_flag & XQC_CONN_FLAG_HANDSHAKE_DONE_ACKED)
&& c->conn_flag & XQC_CONN_FLAG_HANDSHAKE_DONE_SENT)
{
send_hsd = XQC_TRUE;
}
xqc_list_for_each_safe(pos, next, sndq) {
packet_out = xqc_list_entry(pos, xqc_packet_out_t, po_list);
if (xqc_send_ctl_indirectly_ack_or_drop_po(c, packet_out)) {
continue;
}
if (!xqc_packet_out_can_pto_probe(packet_out, path->path_id)) {
continue;
}
if (XQC_IS_ACK_ELICITING(packet_out->po_frame_types)
&& (XQC_NEED_REPAIR(packet_out->po_frame_types)
|| (packet_out->po_flag & XQC_POF_NOTIFY)
|| (packet_out->po_frame_types & XQC_FRAME_BIT_DATAGRAM
&& c->conn_settings.datagram_force_retrans_on)))
{
/* if HSK_DONE is not confirmed, will skip all the pkts do not
contain HSK_DONE frame, until a pkt with HSK_DONE is found, make
HSK_DONE is always with the highest priority */
if (send_hsd
&& !(packet_out->po_frame_types & XQC_FRAME_BIT_HANDSHAKE_DONE))
{
if (packet_out_later_send == NULL) {
/* remember the first ack-eliciting pkt if pkt with HSK_DONE
frame is not the first one */
packet_out_later_send = packet_out;
}
continue;
}
has_reinjection = has_reinjection || xqc_conn_send_probe_pkt(c, path, packet_out);
packet_out_last_sent = packet_out;
if (--probe_num == 0) {
break;
}
/* if a pkt with HSK_DONE is after any other ack-eliciting pkts is
sent, try to send the first ack-eliciting pkt */
if (send_hsd &&
(packet_out->po_frame_types & XQC_FRAME_BIT_HANDSHAKE_DONE))
{
send_hsd = XQC_FALSE;
/* try to send the first ack-eliciting pkt do not contain
HSK_DONE frame */
if (packet_out_later_send) {
has_reinjection = has_reinjection || xqc_conn_send_probe_pkt(c, path, packet_out_later_send);
packet_out_last_sent = packet_out_later_send;
packet_out_later_send = NULL;
if (--probe_num == 0) {
break;
}
}
}
}
}
if (probe_num > 0) {
if (packet_out_last_sent) {
/* at least one pkt was sent, and there is still budget for send
more ack-eliciting pkts, try to send the pkt again */
while (probe_num > 0) {
xqc_log(c->log, XQC_LOG_DEBUG, "|dup pkt on PTO, pkt_num:%ui|",
packet_out_last_sent->po_pkt.pkt_num);
has_reinjection = has_reinjection || xqc_conn_send_probe_pkt(c, path, packet_out_last_sent);
probe_num--;
}
} else {
/* if no packet was sent, try to send PING frame */
while (probe_num > 0) {
xqc_log(c->log, XQC_LOG_DEBUG, "|PING on PTO, cnt: %d|", probe_num);
xqc_path_send_ping_to_probe(path, pns, XQC_PATH_SPECIFIED_BY_PTO);
probe_num--;
}
}
}
if (has_reinjection) {
xqc_path_ctx_t *path;
xqc_list_for_each_safe(pos, next, &c->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
xqc_list_splice_tail_init(&path->path_reinj_tmp_buf,
&path->path_schedule_buf[XQC_SEND_TYPE_NORMAL]);
}
}
}
/* used by client to break amplification limit at server, or to prove address ownership */
void
xqc_conn_send_one_ack_eliciting_pkt(xqc_connection_t *conn, xqc_pkt_num_space_t pns)
{
/* PING will be put into send list */
xqc_conn_gen_ping(conn, pns);
}
xqc_int_t
xqc_conn_check_handshake_completed(xqc_connection_t *conn)
{
return ((conn->conn_flag & XQC_CONN_FLAG_HANDSHAKE_COMPLETED) != 0);
}
xqc_int_t
xqc_conn_is_handshake_confirmed(xqc_connection_t *conn)
{
return ((conn->conn_flag & XQC_CONN_FLAG_HANDSHAKE_CONFIRMED) != 0);
}
xqc_int_t
xqc_conn_close(xqc_engine_t *engine, const xqc_cid_t *cid)
{
xqc_int_t ret;
xqc_connection_t *conn;
conn = xqc_engine_conns_hash_find(engine, cid, 's');
if (!conn) {
xqc_log(engine->log, XQC_LOG_ERROR, "|can not find connection|cid:%s",
xqc_scid_str(engine, cid));
return -XQC_ECONN_NFOUND;
}
xqc_log(conn->log, XQC_LOG_INFO, "|conn:%p|state:%s|flag:%s|", conn,
xqc_conn_state_2_str(conn->conn_state), xqc_conn_flag_2_str(conn, conn->conn_flag));
XQC_CONN_CLOSE_MSG(conn, "local close");
if (conn->conn_state >= XQC_CONN_STATE_DRAINING) {
return XQC_OK;
}
/* close connection after all data sent and acked or XQC_TIMER_LINGER_CLOSE timeout */
xqc_usec_t now = xqc_monotonic_timestamp();
xqc_usec_t pto = xqc_conn_get_max_pto(conn);
if (conn->conn_settings.linger.linger_on && !xqc_send_queue_out_queue_empty(conn->conn_send_queue)) {
conn->conn_flag |= XQC_CONN_FLAG_LINGER_CLOSING;
xqc_usec_t linger_timeout = conn->conn_settings.linger.linger_timeout;
xqc_timer_set(&conn->conn_timer_manager, XQC_TIMER_LINGER_CLOSE, now,
(linger_timeout ? linger_timeout : 3 * pto));
goto end;
}
ret = xqc_conn_immediate_close(conn);
if (ret) {
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_conn_immediate_close error|ret:%d|", ret);
return ret;
}
end:
xqc_engine_remove_wakeup_queue(engine, conn);
xqc_engine_add_active_queue(engine, conn);
xqc_engine_wakeup_once(engine);
return XQC_OK;
}
xqc_int_t
xqc_conn_close_with_error(xqc_connection_t *conn, uint64_t err_code)
{
XQC_CONN_ERR(conn, err_code);
return XQC_OK;
}
xqc_int_t
xqc_conn_get_errno(xqc_connection_t *conn)
{
return conn->conn_err;
}
void *
xqc_conn_get_ssl(xqc_connection_t *conn)
{
if (conn->tls) {
return xqc_tls_get_ssl(conn->tls);
}
return NULL;
}
/* cleanup connection and wait for draining */
void
xqc_conn_shutdown(xqc_connection_t *conn)
{
xqc_path_ctx_t *path;
xqc_list_head_t *pos, *next;
xqc_send_ctl_t *send_ctl;
xqc_usec_t now;
now = xqc_monotonic_timestamp();
xqc_usec_t pto = xqc_conn_get_max_pto(conn);
if (!xqc_timer_is_set(&conn->conn_timer_manager, XQC_TIMER_CONN_DRAINING)) {
xqc_timer_set(&conn->conn_timer_manager, XQC_TIMER_CONN_DRAINING, now, 3 * pto);
}
xqc_send_queue_drop_packets(conn);
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
for (int i = 0; i <= XQC_TIMER_LOSS_DETECTION; i++) {
xqc_timer_unset(&path->path_send_ctl->path_timer_manager, i);
}
}
}
xqc_int_t
xqc_conn_immediate_close(xqc_connection_t *conn)
{
int ret;
if (conn->conn_state >= XQC_CONN_STATE_DRAINING) {
return XQC_OK;
}
if (!(conn->conn_flag & XQC_CONN_FLAG_INIT_RECVD)
&& conn->conn_type == XQC_CONN_TYPE_SERVER)
{
conn->conn_state = XQC_CONN_STATE_CLOSED;
xqc_log_event(conn->log, CON_CONNECTION_STATE_UPDATED, conn);
xqc_conn_log(conn, XQC_LOG_ERROR, "|server cannot send CONNECTION_CLOSE before initial pkt received|");
return XQC_OK;
}
if (conn->conn_state < XQC_CONN_STATE_CLOSING) {
xqc_conn_shutdown(conn);
/* convert state to CLOSING */
xqc_log(conn->log, XQC_LOG_INFO, "|state to closing|state:%s|flags:%s",
xqc_conn_state_2_str(conn->conn_state),
xqc_conn_flag_2_str(conn, conn->conn_flag));
conn->conn_state = XQC_CONN_STATE_CLOSING;
xqc_log_event(conn->log, CON_CONNECTION_STATE_UPDATED, conn);
}
/*
* [Transport] 10.3. Immediate Close, During the closing period, an endpoint that sends a CONNECTION_CLOSE
* frame SHOULD respond to any incoming packet that can be decrypted with another packet containing a CONNECTION_CLOSE
* frame. Such an endpoint SHOULD limit the number of packets it generates containing a CONNECTION_CLOSE frame.
*/
if (conn->conn_close_count < MAX_RSP_CONN_CLOSE_CNT) {
ret = xqc_write_conn_close_to_packet(conn, conn->conn_err);
if (ret) {
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_conn_close_to_packet error|ret:%d|", ret);
}
++conn->conn_close_count;
xqc_log(conn->log, XQC_LOG_DEBUG, "|gen_conn_close|state:%s|", xqc_conn_state_2_str(conn->conn_state));
}
return XQC_OK;
}
xqc_int_t
xqc_conn_send_retry(xqc_connection_t *conn, unsigned char *token, unsigned token_len)
{
xqc_engine_t *engine = conn->engine;
unsigned char buf[XQC_PACKET_OUT_BUF_CAP];
xqc_int_t size = (xqc_int_t)xqc_gen_retry_packet(buf,
conn->dcid_set.current_dcid.cid_buf,
conn->dcid_set.current_dcid.cid_len,
conn->scid_set.user_scid.cid_buf,
conn->scid_set.user_scid.cid_len,
conn->original_dcid.cid_buf,
conn->original_dcid.cid_len,
token, token_len,
XQC_VERSION_V1);
if (size < 0) {
return size;
}
size = (xqc_int_t)conn->transport_cbs.write_socket(
buf, (size_t)size, (struct sockaddr*)conn->peer_addr, conn->peer_addrlen,
xqc_conn_get_user_data(conn));
if (size < 0) {
return size;
}
xqc_log(engine->log, XQC_LOG_INFO, "|<==|xqc_conn_send_retry ok|size:%d|", size);
return XQC_OK;
}
xqc_int_t
xqc_conn_version_check(xqc_connection_t *c, uint32_t version)
{
xqc_engine_t *engine = c->engine;
int i = 0;
if (c->conn_type == XQC_CONN_TYPE_SERVER && c->version == XQC_IDRAFT_INIT_VER) {
uint32_t *list = engine->config->support_version_list;
uint32_t count = engine->config->support_version_count;
if (xqc_uint32_list_find(list, count, version) == -1) {
return -XQC_EPROTO;
}
for (i = XQC_IDRAFT_INIT_VER + 1; i < XQC_IDRAFT_VER_NEGOTIATION; i++) {
if (xqc_proto_version_value[i] == version) {
c->version = i;
xqc_int_t ret = xqc_tls_init(c->tls, c->version, &c->original_dcid);
if (ret != XQC_OK) {
xqc_log(c->log, XQC_LOG_ERROR, "|init tls error|");
return ret;
}
return XQC_OK;
}
}
return -XQC_EPROTO;
}
return XQC_OK;
}
xqc_int_t
xqc_conn_send_version_negotiation(xqc_connection_t *c)
{
xqc_packet_out_t *packet_out = xqc_packet_out_get_and_insert_send(c->conn_send_queue, XQC_PTYPE_VERSION_NEGOTIATION);
if (packet_out == NULL) {
xqc_log(c->log, XQC_LOG_ERROR, "|get XQC_PTYPE_VERSION_NEGOTIATION error|");
return -XQC_EWRITE_PKT;
}
unsigned char *p = packet_out->po_buf;
/* first byte of packet */
*p++ = (1 << 7);
/* version */
*(uint32_t *)p = 0;
p += sizeof(uint32_t);
/* dcid len */
*p = c->dcid_set.current_dcid.cid_len;
++p;
/* dcid */
memcpy(p, c->dcid_set.current_dcid.cid_buf, c->dcid_set.current_dcid.cid_len);
p += c->dcid_set.current_dcid.cid_len;
/* original destination ID len */
*p = c->original_dcid.cid_len;
++p;
/* original destination ID */
memcpy(p, c->original_dcid.cid_buf, c->original_dcid.cid_len);
p += c->original_dcid.cid_len;
/* set supported version list */
uint32_t *version_list = c->engine->config->support_version_list;
uint32_t version_count = c->engine->config->support_version_count;
unsigned char *end = packet_out->po_buf + packet_out->po_buf_size;
for (size_t i = 0; i < version_count; ++i) {
if (p + sizeof(uint32_t) <= end) {
*(uint32_t*)p = htonl(version_list[i]);
p += sizeof(uint32_t);
} else {
break;
}
}
/* set used size of packet */
packet_out->po_used_size = p - packet_out->po_buf;
/* push to conns queue */
xqc_engine_remove_wakeup_queue(c->engine, c);
xqc_engine_add_active_queue(c->engine, c);
c->conn_flag &= ~XQC_CONN_FLAG_VERSION_NEGOTIATION;
return XQC_OK;
}
void
xqc_conn_continue_send_by_conn(xqc_connection_t *conn)
{
if (!conn) {
return;
}
xqc_log(conn->log, XQC_LOG_DEBUG, "|conn:%p|", conn);
xqc_engine_remove_wakeup_queue(conn->engine, conn);
xqc_engine_add_active_queue(conn->engine, conn);
xqc_engine_conn_logic(conn->engine, conn);
}
int
xqc_conn_continue_send(xqc_engine_t *engine, const xqc_cid_t *cid)
{
xqc_connection_t *conn = xqc_engine_conns_hash_find(engine, cid, 's');
if (!conn) {
xqc_log(engine->log, XQC_LOG_ERROR, "|can not find connection|cid:%s",
xqc_scid_str(engine, cid));
return -XQC_ECONN_NFOUND;
}
xqc_conn_continue_send_by_conn(conn);
return XQC_OK;
}
void
xqc_conn_encode_mp_settings(xqc_connection_t *conn, char *buf, size_t buf_sz)
{
size_t len = 0;
uint8_t encode_val = 0;
if (buf_sz < XQC_MP_SETTINGS_STR_LEN) {
return;
}
if (conn->enable_multipath) {
encode_val = 1;
}
//len: 1
len = snprintf(buf, buf_sz, "%d", encode_val);
encode_val = 0;
if (conn->local_settings.enable_multipath) {
encode_val = 1;
}
//len: 3
len += snprintf(buf + len, buf_sz - len, "/%d", encode_val);
encode_val = 0;
if (conn->remote_settings.enable_multipath) {
encode_val = 1;
}
//len: 5
len += snprintf(buf + len, buf_sz - len, "/%d", encode_val);
//len: 9
encode_val = conn->local_settings.multipath_version;
len += snprintf(buf + len, buf_sz - len, "/%x", encode_val);
//len: 13
encode_val = conn->remote_settings.multipath_version;
len += snprintf(buf + len, buf_sz - len, "/%x", encode_val);
buf[len] = 0;
}
void
xqc_conn_info_print(xqc_connection_t *conn, xqc_conn_stats_t *conn_stats)
{
char *buff = conn_stats->conn_info;
size_t buff_size = XQC_CONN_INFO_LEN;
size_t curr_size = 0;
int ret = 0;
int i = 0;
xqc_list_head_t *pos, *next;
xqc_path_ctx_t *path = NULL;
xqc_path_info_t path_info;
char mp_settings[XQC_MP_SETTINGS_STR_LEN] = {0};
uint32_t sock_err_flag = 0;
uint32_t init_cwnd = 0;
xqc_conn_encode_mp_settings(conn, mp_settings, XQC_MP_SETTINGS_STR_LEN);
init_cwnd = conn->conn_settings.cc_params.customize_on ? conn->conn_settings.cc_params.init_cwnd : 0;
/* conn info */
ret = snprintf(buff, buff_size, "%s,%u,%u,%u,%u,%u,%u,"
"%u,%u,%u,%u,%u,%u,%u,%"PRIu64",%"PRIu64",%"PRIu64",i%u,"
,
mp_settings,
conn->create_path_count,
conn->validated_path_count,
conn->active_path_count,
conn->dgram_stats.total_dgram,
conn->dgram_stats.hp_dgram,
conn->dgram_stats.hp_red_dgram,
conn->dgram_stats.hp_red_dgram_mp,
conn->dgram_stats.timer_red_dgram,
conn->sched_cc_blocked,
conn->send_cc_blocked,
conn->snd_pkt_stats.conn_sent_pkts,
conn->rcv_pkt_stats.conn_rcvd_pkts,
conn->rcv_pkt_stats.conn_udp_pkts,
conn->stream_stats.send_bytes,
conn->stream_stats.reinjected_bytes,
conn->stream_stats.recv_bytes,
init_cwnd
);
curr_size += ret;
if (curr_size >= buff_size) {
goto full;
}
/* recv_stats */
for (i = 0; i < 3; i++) {
ret = snprintf(buff + curr_size, buff_size - curr_size,
"%u,%u,%u,%"PRIx64",%"PRIu64","
"%"PRIu64",%d,",
(uint32_t)conn->rcv_pkt_stats.pkt_types[i],
conn->rcv_pkt_stats.pkt_size[i],
conn->rcv_pkt_stats.pkt_udp_size[i],
(uint64_t)conn->rcv_pkt_stats.pkt_frames[i],
(uint64_t)conn->rcv_pkt_stats.pkt_pn[i],
(uint64_t)conn->rcv_pkt_stats.pkt_timestamp[i],
conn->rcv_pkt_stats.pkt_err[i]);
curr_size += ret;
if (curr_size >= buff_size) {
goto full;
}
}
/* send_stats */
for (i = 0; i < 3; i++) {
ret = snprintf(buff + curr_size, buff_size - curr_size,
"%u,%u,%"PRIx64",%"PRIu64","
"%"PRIu64",",
(uint32_t)conn->snd_pkt_stats.pkt_types[i],
conn->snd_pkt_stats.pkt_size[i],
(uint64_t)conn->snd_pkt_stats.pkt_frames[i],
(uint64_t)conn->snd_pkt_stats.pkt_pn[i],
(uint64_t)conn->snd_pkt_stats.pkt_timestamp[i]);
curr_size += ret;
if (curr_size >= buff_size) {
goto full;
}
}
/* path layer 自定义 */
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
xqc_path_record_info(path, &path_info);
sock_err_flag = (path->path_flag & XQC_PATH_FLAG_SOCKET_ERROR) != 0;
ret = snprintf(buff + curr_size, buff_size - curr_size,
"%d-%d,%d-%d,%d-%d,%d-%"PRIu64","
"%d-%"PRIu64",%d-%"PRIu64","
"%d-%"PRIu64",%d-%"PRIu64","
"%d-%u,%d-%u,"
"%d-%u,%d-%u,"
"%d-%u,%d-%u,"
"%d-%u,%d-%u,"
"%d-%u,%d-%u,",
(int)path_info.path_id, path_info.path_state,
(int)path_info.path_id, path_info.app_path_status,
(int)path_info.path_id, sock_err_flag,
(int)path_info.path_id, path_info.path_create_time,
(int)path_info.path_id, path_info.path_destroy_time,
(int)path_info.path_id, path_info.srtt / 1000,
(int)path_info.path_id, path_info.path_bytes_send,
(int)path_info.path_id, path_info.path_bytes_recv,
(int)path_info.path_id, path_info.pkt_send_cnt,
(int)path_info.path_id, path_info.pkt_recv_cnt,
(int)path_info.path_id, path_info.loss_cnt,
(int)path_info.path_id, path_info.tlp_cnt,
(int)path_info.path_id, path_info.dgram_send_cnt,
(int)path_info.path_id, path_info.dgram_recv_cnt,
(int)path_info.path_id, path_info.red_dgram_send_cnt,
(int)path_info.path_id, path_info.red_dgram_recv_cnt,
(int)path_info.path_id, path->rebinding_count,
(int)path_info.path_id, path->rebinding_valid);
curr_size += ret;
if (curr_size >= buff_size) {
goto full;
}
}
full:
curr_size = xqc_min(curr_size, buff_size);
for (i = curr_size - 1; i >= 0; i--) {
if (buff[i] == ',') {
buff[i] = '\0';
break;
}
}
buff[buff_size - 1] = '\0';
}
void
xqc_conn_get_stats_internal(xqc_connection_t *conn, xqc_conn_stats_t *conn_stats)
{
/* 1. 与路径无关的连接级别埋点 */
const char *out_alpn = NULL;
size_t out_alpn_len = 0;
if (conn->tls) {
xqc_tls_get_selected_alpn(conn->tls, &out_alpn, &out_alpn_len);
}
xqc_memset(conn_stats->alpn, 0, XQC_MAX_ALPN_BUF_LEN);
if (out_alpn) {
strncpy(conn_stats->alpn, out_alpn, xqc_min(out_alpn_len, XQC_MAX_ALPN_BUF_LEN));
} else {
conn_stats->alpn[0] = '-';
conn_stats->alpn[1] = '1';
}
conn_stats->conn_err = (int)conn->conn_err;
conn_stats->early_data_flag = XQC_0RTT_NONE;
conn_stats->enable_multipath = conn->enable_multipath;
conn_stats->enable_fec = conn->conn_settings.fec_params.fec_encoder_scheme ? 1 : 0;
conn_stats->spurious_loss_detect_on = conn->conn_settings.spurious_loss_detect_on;
conn_stats->max_acked_mtu = conn->max_acked_po_size;
if (conn->conn_flag & XQC_CONN_FLAG_HAS_0RTT) {
if (conn->conn_flag & XQC_CONN_FLAG_0RTT_OK) {
conn_stats->early_data_flag = XQC_0RTT_ACCEPT;
} else if (conn->conn_flag & XQC_CONN_FLAG_0RTT_REJ) {
conn_stats->early_data_flag = XQC_0RTT_REJECT;
}
}
/* 2. srtt 和 ack_info 使用主路信息 (TODO: 有问题,后续需要修改) */
if (conn->conn_initial_path
&& conn->conn_initial_path->path_send_ctl
&& conn->conn_initial_path->path_pn_ctl)
{
if (conn->conn_initial_path->path_send_ctl->ctl_first_rtt_sample_time == 0) {
conn_stats->srtt = 0;
conn_stats->min_rtt = 0;
} else {
conn_stats->srtt = conn->conn_initial_path->path_send_ctl->ctl_srtt;
conn_stats->min_rtt = conn->conn_initial_path->path_send_ctl->ctl_minrtt;
}
xqc_recv_record_print(conn, &conn->conn_initial_path->path_pn_ctl->ctl_recv_record[XQC_PNS_APP_DATA],
conn_stats->ack_info, sizeof(conn_stats->ack_info));
}
if (conn->fec_ctl) {
conn_stats->send_fec_cnt = conn->fec_ctl->fec_send_repair_num_total;
conn_stats->fec_recover_pkt_cnt = conn->fec_ctl->fec_recover_pkt_cnt;
}
/* 3. 遍历路径,获取各个路径count加和 */
xqc_list_head_t *pos, *next;
xqc_path_ctx_t *path = NULL;
xqc_send_ctl_t *send_ctl;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
if (path == NULL) {
xqc_log(conn->log, XQC_LOG_WARN, "|path NULL|");
continue;
}
send_ctl = path->path_send_ctl;
if (send_ctl == NULL) {
xqc_log(conn->log, XQC_LOG_WARN, "|path_send_ctl NULL|path%ui|", path->path_id);
continue;
}
conn_stats->lost_count += send_ctl->ctl_lost_count;
conn_stats->send_count += send_ctl->ctl_send_count;
conn_stats->tlp_count += send_ctl->ctl_tlp_count;
conn_stats->spurious_loss_count += send_ctl->ctl_spurious_loss_count;
conn_stats->recv_count += send_ctl->ctl_recv_count;
conn_stats->lost_dgram_count += send_ctl->ctl_lost_dgram_cnt;
conn_stats->inflight_bytes += send_ctl->ctl_bytes_in_flight;
conn_stats->total_rebind_count += path->rebinding_count;
conn_stats->total_rebind_valid += path->rebinding_valid;
}
/* 路径信息 */
xqc_conn_path_metrics_print(conn, conn_stats);
/* 自定义信息 */
xqc_conn_info_print(conn, conn_stats);
xqc_extern_conn_info_print(conn, conn_stats);
}
xqc_conn_stats_t
xqc_conn_get_stats(xqc_engine_t *engine, const xqc_cid_t *cid)
{
xqc_connection_t *conn;
xqc_conn_stats_t conn_stats;
xqc_memzero(&conn_stats, sizeof(conn_stats));
for (int i = 0; i < XQC_MAX_PATHS_COUNT; ++i) {
conn_stats.paths_info[i].path_id = XQC_MAX_UINT64_VALUE;
conn_stats.paths_info[i].path_app_status = 0;
}
conn = xqc_engine_conns_hash_find(engine, cid, 's');
if (!conn) {
xqc_log(engine->log, XQC_LOG_ERROR, "|can not find connection|cid:%s",
xqc_scid_str(engine, cid));
return conn_stats;
}
xqc_conn_get_stats_internal(conn, &conn_stats);
return conn_stats;
}
xqc_conn_qos_stats_t
xqc_conn_get_qos_stats(xqc_engine_t *engine, const xqc_cid_t *cid)
{
xqc_connection_t *conn;
xqc_conn_qos_stats_t qos_stats;
xqc_memzero(&qos_stats, sizeof(qos_stats));
conn = xqc_engine_conns_hash_find(engine, cid, 's');
if (!conn) {
xqc_log(engine->log, XQC_LOG_ERROR, "|can not find connection|cid:%s",
xqc_scid_str(engine, cid));
return qos_stats;
}
/* 2. srtt 和 ack_info 使用主路信息 (TODO: 有问题,后续需要修改) */
if (conn->conn_initial_path
&& conn->conn_initial_path->path_send_ctl
&& conn->conn_initial_path->path_pn_ctl)
{
if (conn->conn_initial_path->path_send_ctl->ctl_first_rtt_sample_time == 0) {
qos_stats.srtt = 0;
qos_stats.min_rtt = 0;
} else {
qos_stats.srtt = conn->conn_initial_path->path_send_ctl->ctl_srtt;
qos_stats.min_rtt = conn->conn_initial_path->path_send_ctl->ctl_minrtt;
}
}
xqc_list_head_t *pos, *next;
xqc_path_ctx_t *path = NULL;
xqc_send_ctl_t *send_ctl;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
if (path == NULL) {
continue;
}
send_ctl = path->path_send_ctl;
if (send_ctl == NULL) {
continue;
}
qos_stats.inflight_bytes += send_ctl->ctl_bytes_in_flight;
}
return qos_stats;
}
xqc_usec_t
xqc_conn_get_lastest_rtt(xqc_engine_t *engine, const xqc_cid_t *cid)
{
xqc_connection_t *conn;
xqc_path_ctx_t *path;
conn = xqc_engine_conns_hash_find(engine, cid, 's');
if (!conn) {
xqc_log(engine->log, XQC_LOG_ERROR, "|can not find connection|cid:%s",
xqc_scid_str(engine, cid));
return 0;
}
path = conn->conn_initial_path;
if (!path) {
xqc_log(engine->log, XQC_LOG_ERROR, "|can not find initial path|cid:%s",
xqc_scid_str(engine, cid));
return 0;
}
return path->path_send_ctl->ctl_latest_rtt;
}
xqc_int_t
xqc_conn_check_token(xqc_connection_t *conn, const unsigned char *token, unsigned token_len)
{
if (token_len > XQC_MAX_TOKEN_LEN) {
xqc_log(conn->log, XQC_LOG_ERROR, "|%ud exceed XQC_MAX_TOKEN_LEN|", token_len);
return XQC_ERROR;
} else if (token_len == 0) {
xqc_log(conn->log, XQC_LOG_INFO, "|token empty|");
return XQC_ERROR;
}
struct sockaddr *sa = (struct sockaddr *)conn->peer_addr;
const unsigned char *pos = token;
if (*pos++ & 0x80) {
struct in6_addr *in6 = (struct in6_addr *)pos;
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
if (token_len != 21) {
xqc_log(conn->log, XQC_LOG_ERROR, "|token_len error|token_len:%ui|", token_len);
return XQC_ERROR;
}
if (memcmp(&sa6->sin6_addr, in6, sizeof(struct in6_addr)) != 0) {
xqc_log(conn->log, XQC_LOG_WARN, "|ipv6 not match|");
return XQC_ERROR;
}
pos += sizeof(struct in6_addr);
} else {
struct in_addr *in4 = (struct in_addr *)pos;
struct sockaddr_in *sa4 = (struct sockaddr_in *)sa;
if (token_len != 9) {
xqc_log(conn->log, XQC_LOG_ERROR, "|token_len error|token_len:%ui|", token_len);
return XQC_ERROR;
}
xqc_log(conn->log, XQC_LOG_DEBUG, "|peer_addr:%s|", inet_ntoa(sa4->sin_addr));
if (memcmp(&sa4->sin_addr, pos, sizeof(struct in_addr)) != 0) {
xqc_log(conn->log, XQC_LOG_INFO, "|ipv4 not match|token_addr:%s|", inet_ntoa(*in4));
return XQC_ERROR;
}
pos += sizeof(struct in_addr);
}
/* check token lifetime */
uint32_t *expire = (uint32_t *)pos;
*expire = ntohl(*expire);
uint64_t now = xqc_monotonic_timestamp() / 1000000;
if (*expire < now) {
xqc_log(conn->log, XQC_LOG_INFO, "|token_expire|expire:%ud|now:%ui|", *expire, now);
return XQC_ERROR;
} else if (*expire - now <= XQC_TOKEN_UPDATE_DELTA) {
xqc_log(conn->log, XQC_LOG_DEBUG, "|new token|expire:%ud|now:%ui|delta:%ud|",
*expire, now, XQC_TOKEN_UPDATE_DELTA);
conn->conn_flag |= XQC_CONN_FLAG_UPDATE_NEW_TOKEN;
}
xqc_log(conn->log, XQC_LOG_DEBUG, "|pass|");
return XQC_OK;
}
/*
* +-+-+-+-+-+-+-+-+
* |v|0|0|0|0|0|0|0|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | IP(32/128) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Expire Time(32) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* v: 0 For IPv4, 1 For IPv6
*/
void
xqc_conn_gen_token(xqc_connection_t *conn, unsigned char *token, unsigned *token_len)
{
struct sockaddr *sa = (struct sockaddr *)conn->peer_addr;
if (sa->sa_family == AF_INET) {
*token++ = 0x00;
struct sockaddr_in *sa4 = (struct sockaddr_in *)sa;
memcpy(token, &sa4->sin_addr, sizeof(struct in_addr));
token += sizeof(struct in_addr);
*token_len = 9;
} else {
*token++ = 0x80;
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
memcpy(token, &sa6->sin6_addr, sizeof(struct in6_addr));
token += sizeof(struct in6_addr);
*token_len = 21;
}
uint32_t expire = xqc_monotonic_timestamp() / 1000000 + XQC_TOKEN_EXPIRE_DELTA;
xqc_log(conn->log, XQC_LOG_DEBUG, "|expire:%ud|", expire);
expire = htonl(expire);
memcpy(token, &expire, sizeof(expire));
}
void
xqc_conn_resend_0rtt_datagram(xqc_connection_t *conn)
{
xqc_list_head_t *pos, *next;
xqc_datagram_0rtt_buffer_t *dgram_buffer;
struct iovec iov[XQC_MAX_SEND_MSG_ONCE];
uint64_t dgram_id_list[XQC_MAX_SEND_MSG_ONCE];
size_t iov_size, sent, sent_bytes;
int ret;
iov_size = 0;
xqc_list_for_each_safe(pos, next, &conn->dgram_0rtt_buffer_list) {
dgram_buffer = xqc_list_entry(pos, xqc_datagram_0rtt_buffer_t, list);
iov[iov_size].iov_base = dgram_buffer->iov.iov_base;
iov[iov_size].iov_len = dgram_buffer->iov.iov_len;
dgram_id_list[iov_size] = dgram_buffer->dgram_id;
iov_size++;
if (iov_size >= XQC_MAX_SEND_MSG_ONCE) {
ret = xqc_datagram_send_multiple_internal(conn, iov, dgram_id_list, XQC_MAX_SEND_MSG_ONCE, &sent, &sent_bytes, dgram_buffer->qos_level, XQC_TRUE);
if (ret < 0) {
xqc_log(conn->log, XQC_LOG_ERROR, "|unable_to_resend_0rtt_pkts_in_1rtt_way|");
XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR);
iov_size = 0;
break;
}
iov_size -= XQC_MAX_SEND_MSG_ONCE;
}
}
if (iov_size > 0) {
ret = xqc_datagram_send_multiple_internal(conn, iov, dgram_id_list, iov_size, &sent, &sent_bytes, dgram_buffer->qos_level, XQC_TRUE);
if (ret < 0) {
xqc_log(conn->log, XQC_LOG_ERROR, "|unbale_to_resend_0rtt_pkts_in_1rtt_way|");
XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR);
}
}
xqc_conn_destroy_0rtt_datagram_buffer_list(conn);
}
xqc_int_t
xqc_conn_early_data_reject(xqc_connection_t *conn)
{
xqc_list_head_t *pos, *next;
xqc_stream_t *stream;
xqc_log(conn->log, XQC_LOG_DEBUG, "|reject|");
conn->conn_flag |= XQC_CONN_FLAG_0RTT_REJ;
if (conn->conn_type == XQC_CONN_TYPE_SERVER) {
xqc_packet_in_t *packet_in;
xqc_list_for_each_safe(pos, next, &conn->undecrypt_packet_in[XQC_ENC_LEV_0RTT]) {
packet_in = xqc_list_entry(pos, xqc_packet_in_t, pi_list);
xqc_list_del_init(pos);
xqc_packet_in_destroy(packet_in, conn);
}
return XQC_OK;
}
xqc_send_queue_drop_0rtt_packets(conn);
xqc_conn_resend_0rtt_datagram(conn);
xqc_list_for_each_safe(pos, next, &conn->conn_all_streams) {
stream = xqc_list_entry(pos, xqc_stream_t, all_stream_list);
if (stream->stream_flag & XQC_STREAM_FLAG_HAS_0RTT) {
stream->stream_send_offset = 0;
stream->stream_unacked_pkt = 0;
if (stream->stream_state_send >= XQC_SEND_STREAM_ST_RESET_SENT
|| stream->stream_state_recv >= XQC_RECV_STREAM_ST_RESET_RECVD)
{
xqc_destroy_write_buff_list(&stream->stream_write_buff_list.write_buff_list);
return XQC_OK;
}
xqc_stream_send_state_update(stream, XQC_SEND_STREAM_ST_READY);
xqc_stream_recv_state_update(stream, XQC_RECV_STREAM_ST_RECV);
xqc_stream_write_buffed_data_to_packets(stream);
}
}
return XQC_OK;
}
xqc_int_t
xqc_conn_early_data_accept(xqc_connection_t *conn)
{
xqc_list_head_t *pos, *next;
xqc_stream_t *stream;
xqc_log(conn->log, XQC_LOG_DEBUG, "|accept|");
conn->conn_flag |= XQC_CONN_FLAG_0RTT_OK;
if (conn->conn_type == XQC_CONN_TYPE_SERVER) {
return XQC_OK;
}
xqc_list_for_each_safe(pos, next, &conn->conn_all_streams) {
stream = xqc_list_entry(pos, xqc_stream_t, all_stream_list);
xqc_destroy_write_buff_list(&stream->stream_write_buff_list.write_buff_list);
}
xqc_conn_destroy_0rtt_datagram_buffer_list(conn);
return XQC_OK;
}
xqc_bool_t
xqc_conn_is_ready_to_send_early_data(xqc_connection_t *conn)
{
return xqc_tls_is_ready_to_send_early_data(conn->tls);
}
xqc_int_t
xqc_conn_handshake_confirmed(xqc_connection_t *conn)
{
if (!(conn->conn_flag & XQC_CONN_FLAG_HANDSHAKE_CONFIRMED)) {
xqc_log(conn->log, XQC_LOG_INFO, "|handshake confirmed|conn:%p|", conn);
conn->conn_flag |= XQC_CONN_FLAG_HANDSHAKE_CONFIRMED;
xqc_send_queue_drop_handshake_packets(conn);
}
return XQC_OK;
}
void
xqc_conn_update_flow_ctl_settings(xqc_connection_t *conn)
{
xqc_conn_flow_ctl_t *flow_ctl = &conn->conn_flow_ctl;
xqc_trans_settings_t *remote_settings = &conn->remote_settings;
flow_ctl->fc_max_data_can_send = remote_settings->max_data;
flow_ctl->fc_max_streams_bidi_can_send = remote_settings->max_streams_bidi;
flow_ctl->fc_max_streams_uni_can_send = remote_settings->max_streams_uni;
}
xqc_int_t
xqc_conn_add_path_cid_sets(xqc_connection_t *conn, uint32_t start, uint32_t end)
{
if (conn->enable_multipath) {
uint32_t path_id;
xqc_int_t ret;
/* add cid_set_inner for all paths */
for (path_id = start; path_id <= end; path_id++) {
ret = xqc_cid_set_add_path(&conn->scid_set, path_id);
if (ret != XQC_OK) {
return ret;
}
ret = xqc_cid_set_add_path(&conn->dcid_set, path_id);
if (ret != XQC_OK) {
return ret;
}
}
}
return XQC_OK;
}
void
xqc_conn_try_to_enable_pmtud(xqc_connection_t *conn)
{
uint64_t pmtud_check_bit = conn->conn_type == XQC_CONN_TYPE_SERVER ? 0x2 : 0x1;
xqc_usec_t now;
if (((conn->remote_settings.enable_pmtud & conn->local_settings.enable_pmtud) & pmtud_check_bit) != 0) {
conn->enable_pmtud = 1;
now = xqc_monotonic_timestamp();
if (conn->enable_multipath) {
xqc_timer_set(&conn->conn_timer_manager, XQC_TIMER_PMTUD_PROBING, now, XQC_PMTUD_START_DELAY * 1000);
} else {
xqc_timer_set(&conn->conn_timer_manager, XQC_TIMER_PMTUD_PROBING, now, 1);
}
}
}
xqc_int_t
xqc_conn_try_to_enable_multipath(xqc_connection_t *conn)
{
xqc_int_t ret = XQC_OK;
uint64_t path_id = 0;
/* determine multipath mode */
conn->enable_multipath = xqc_conn_enable_multipath(conn);
conn->conn_settings.multipath_version = xqc_conn_multipath_version_negotiation(conn);
if (conn->conn_settings.multipath_version == XQC_ERR_MULTIPATH_VERSION) {
xqc_log(conn->log, XQC_LOG_WARN, "|multipath_version_negotiation err|");
conn->enable_multipath = 0;
}
if (conn->enable_multipath) {
conn->conn_flag |= XQC_CONN_FLAG_MP_WAIT_MP_READY;
}
return xqc_conn_add_path_cid_sets(conn, XQC_INITIAL_PATH_ID + 1, conn->curr_max_path_id);
}
xqc_int_t
xqc_conn_handshake_complete(xqc_connection_t *conn)
{
xqc_int_t ret;
xqc_list_head_t *pos, *next;
xqc_stream_t *stream;
xqc_multipath_version_t mp_version_ret;
/* conn's handshake is complete when TLS stack has reported handshake complete */
conn->conn_flag |= XQC_CONN_FLAG_HANDSHAKE_COMPLETED;
if (conn->conn_type == XQC_CONN_TYPE_SERVER) {
/* the TLS handshake is considered confirmed at the server when the handshake completes */
xqc_conn_handshake_confirmed(conn);
/* send handshake_done immediately */
ret = xqc_write_handshake_done_frame_to_packet(conn);
if (ret < 0) {
xqc_log(conn->log, XQC_LOG_WARN, "|write_handshake_done err|");
return ret;
}
/* if client sent no token or sent an invalid token, server sends a NEW_TOKEN frame */
if (!(conn->conn_flag & XQC_CONN_FLAG_TOKEN_OK)
|| conn->conn_flag & XQC_CONN_FLAG_UPDATE_NEW_TOKEN)
{
xqc_write_new_token_to_packet(conn);
}
} else {
/*
* client MUST discard Initial keys when it first sends a Handshake packet,
* equivalent to handshake complete and can send 1RTT
*/
xqc_send_queue_drop_initial_packets(conn);
}
/* 0RTT rejected, send in 1RTT again */
if ((conn->conn_flag & XQC_CONN_FLAG_HANDSHAKE_COMPLETED)
&& ((conn->conn_type == XQC_CONN_TYPE_CLIENT && conn->conn_flag & XQC_CONN_FLAG_HAS_0RTT)
|| conn->conn_type == XQC_CONN_TYPE_SERVER)
&& !(conn->conn_flag & XQC_CONN_FLAG_0RTT_OK)
&& !(conn->conn_flag & XQC_CONN_FLAG_0RTT_REJ))
{
int accept = xqc_tls_is_early_data_accepted(conn->tls);
if (accept == XQC_TLS_EARLY_DATA_REJECT) {
xqc_conn_early_data_reject(conn);
} else if (accept == XQC_TLS_EARLY_DATA_ACCEPT) {
xqc_conn_early_data_accept(conn);
}
}
return XQC_OK;
}
xqc_int_t
xqc_conn_buff_undecrypt_packet_in(xqc_packet_in_t *packet_in,
xqc_connection_t *conn, xqc_encrypt_level_t encrypt_level)
{
if (conn->undecrypt_count[encrypt_level] >= XQC_UNDECRYPT_PACKET_MAX
|| packet_in->buf_size > XQC_MAX_PACKET_IN_LEN)
{
xqc_log(conn->log, XQC_LOG_WARN,
"|delay|XQC_ELIMIT|undecrypt_count:%ud|encrypt_level:%d|buf_size:%uz|",
conn->undecrypt_count[encrypt_level], encrypt_level, packet_in->buf_size);
return -XQC_ELIMIT;
}
/* limit the buffered 0-RTT packet count before a valid client Initial packet is received */
if (conn->conn_type == XQC_CONN_TYPE_SERVER
&& !(conn->conn_flag & XQC_CONN_FLAG_DCID_OK)
&& encrypt_level == XQC_ENC_LEV_0RTT
&& conn->undecrypt_count[encrypt_level] > XQC_UNDECRYPT_0RTT_MAX_BEFORE_INIT)
{
xqc_log(conn->log, XQC_LOG_WARN, "|0RTT reach buffer limit before DCID confirmed|");
return -XQC_ELIMIT;
}
xqc_packet_in_t *new_packet = xqc_calloc(1, sizeof(xqc_packet_in_t));
if (new_packet == NULL) {
return -XQC_EMALLOC;
}
new_packet->buf = xqc_malloc(XQC_MAX_PACKET_IN_LEN);
if (new_packet->buf == NULL) {
xqc_free(new_packet);
return -XQC_EMALLOC;
}
new_packet->pi_pkt = packet_in->pi_pkt;
new_packet->buf_size = packet_in->buf_size;
xqc_memcpy((unsigned char *)new_packet->buf, packet_in->buf, packet_in->buf_size);
new_packet->pos = (unsigned char *)new_packet->buf + (packet_in->pos - packet_in->buf);
new_packet->last = (unsigned char *)new_packet->buf + (packet_in->last - packet_in->buf);
new_packet->pkt_recv_time = packet_in->pkt_recv_time;
xqc_list_add_tail(&new_packet->pi_list, &conn->undecrypt_packet_in[encrypt_level]);
conn->undecrypt_count[encrypt_level]++;
xqc_log_event(conn->log, TRA_PACKET_BUFFERED, new_packet);
xqc_log(conn->log, XQC_LOG_DEBUG, "|====>|delay|undecrypt_count:%ud|encrypt_level:%d|",
conn->undecrypt_count[encrypt_level], encrypt_level);
return XQC_OK;
}
xqc_int_t
xqc_conn_process_undecrypt_packet_in(xqc_connection_t *conn, xqc_encrypt_level_t encrypt_level)
{
if (conn->undecrypt_count[encrypt_level] == 0) {
return XQC_OK;
}
xqc_packet_in_t *packet_in;
xqc_list_head_t *pos, *next;
xqc_int_t ret;
xqc_list_for_each_safe(pos, next, &conn->undecrypt_packet_in[encrypt_level]) {
packet_in = xqc_list_entry(pos, xqc_packet_in_t, pi_list);
xqc_log_event(conn->log, TRA_DATAGRAMS_RECEIVED, packet_in->buf_size, packet_in->pi_path_id);
xqc_log(conn->log, XQC_LOG_DEBUG, "|delay|undecrypt_count:%ud|encrypt_level:%d|",
conn->undecrypt_count[encrypt_level], encrypt_level);
ret = xqc_conn_process_packet(conn, packet_in->buf, packet_in->buf_size, packet_in->pkt_recv_time);
if (ret) {
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_packet_process error|ret:%d|", ret);
return ret;
}
xqc_list_del_init(pos);
xqc_packet_in_destroy(packet_in, conn);
conn->undecrypt_count[encrypt_level]--;
}
return XQC_OK;
}
void
xqc_conn_buff_1rtt_packet(xqc_connection_t *conn, xqc_packet_out_t *po)
{
xqc_send_queue_remove_send(&po->po_list);
xqc_send_queue_insert_buff(&po->po_list, &conn->conn_send_queue->sndq_buff_1rtt_packets);
if (!(conn->conn_flag & XQC_CONN_FLAG_DCID_OK)) {
po->po_flag |= XQC_POF_DCID_NOT_DONE;
}
}
void
xqc_conn_buff_1rtt_packets(xqc_connection_t *conn)
{
xqc_packet_out_t *packet_out;
xqc_list_head_t *pos, *next;
xqc_list_for_each_safe(pos, next, &conn->conn_send_queue->sndq_send_packets) {
packet_out = xqc_list_entry(pos, xqc_packet_out_t, po_list);
if (packet_out->po_pkt.pkt_type == XQC_PTYPE_SHORT_HEADER) {
xqc_send_queue_remove_send(&packet_out->po_list);
xqc_send_queue_insert_buff(&packet_out->po_list, &conn->conn_send_queue->sndq_buff_1rtt_packets);
if (!(conn->conn_flag & XQC_CONN_FLAG_DCID_OK)) {
packet_out->po_flag |= XQC_POF_DCID_NOT_DONE;
}
}
}
}
void
xqc_conn_write_buffed_1rtt_packets(xqc_connection_t *conn)
{
if (conn->conn_flag & XQC_CONN_FLAG_CAN_SEND_1RTT) {
xqc_send_queue_t *send_queue = conn->conn_send_queue;
xqc_list_head_t *pos, *next;
xqc_packet_out_t *packet_out;
unsigned total = 0;
xqc_list_for_each_safe(pos, next, &send_queue->sndq_buff_1rtt_packets) {
packet_out = xqc_list_entry(pos, xqc_packet_out_t, po_list);
xqc_send_queue_remove_buff(pos, send_queue);
xqc_send_queue_insert_send(packet_out, &send_queue->sndq_send_packets, send_queue);
if (packet_out->po_flag & XQC_POF_DCID_NOT_DONE) {
xqc_short_packet_update_dcid(packet_out, conn->dcid_set.current_dcid);
}
++total;
}
xqc_log(conn->log, XQC_LOG_DEBUG, "|total:%ui|", total);
}
}
xqc_usec_t
xqc_conn_next_wakeup_time(xqc_connection_t *conn)
{
xqc_usec_t min_time = XQC_MAX_UINT64_VALUE;
xqc_usec_t wakeup_time;
xqc_timer_t *timer;
for (xqc_timer_type_t type = 0; type < XQC_TIMER_N; ++type) {
timer = &conn->conn_timer_manager.timer[type];
if (timer->timer_is_set) {
min_time = xqc_min(min_time, timer->expire_time);
}
}
xqc_list_head_t *pos, *next;
xqc_gp_timer_t *gp_timer;
/* gp timer */
xqc_list_for_each_safe(pos, next, &conn->conn_timer_manager.gp_timer_list) {
gp_timer = xqc_list_entry(pos, xqc_gp_timer_t, list);
if (gp_timer->timer_is_set) {
min_time = xqc_min(min_time, gp_timer->expire_time);
}
}
xqc_path_ctx_t *path;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
if (path->path_state != XQC_PATH_STATE_ACTIVE) {
continue;
}
for (xqc_timer_type_t type = 0; type < XQC_TIMER_N; ++type) {
timer = &path->path_send_ctl->path_timer_manager.timer[type];
if (timer->timer_is_set) {
min_time = xqc_min(min_time, timer->expire_time);
}
}
}
wakeup_time = min_time == XQC_MAX_UINT64_VALUE ? 0 : min_time;
xqc_log(conn->log, XQC_LOG_DEBUG, "|wakeup_time:%ui|", wakeup_time);
return wakeup_time;
}
static char g_local_addr_str[INET6_ADDRSTRLEN];
static char g_peer_addr_str[INET6_ADDRSTRLEN];
char *
xqc_local_addr_str(xqc_engine_t *engine, const struct sockaddr *local_addr, socklen_t local_addrlen)
{
if (local_addrlen == 0 || local_addr == NULL) {
engine->local_addr_str[0] = '\0';
return engine->local_addr_str;
}
struct sockaddr_in *sa_local = (struct sockaddr_in *)local_addr;
if (sa_local->sin_family == AF_INET) {
if (inet_ntop(sa_local->sin_family, &sa_local->sin_addr, engine->local_addr_str, local_addrlen) == NULL) {
engine->local_addr_str[0] = '\0';
}
} else {
if (inet_ntop(sa_local->sin_family, &((struct sockaddr_in6*)sa_local)->sin6_addr,
engine->local_addr_str, local_addrlen) == NULL)
{
engine->local_addr_str[0] = '\0';
}
}
return engine->local_addr_str;
}
char *
xqc_peer_addr_str(xqc_engine_t *engine, const struct sockaddr *peer_addr, socklen_t peer_addrlen)
{
if (peer_addrlen == 0 || peer_addr == NULL) {
engine->peer_addr_str[0] = '\0';
return engine->peer_addr_str;
}
struct sockaddr_in *sa_peer = (struct sockaddr_in *)peer_addr;
if (sa_peer->sin_family == AF_INET) {
if (inet_ntop(sa_peer->sin_family, &sa_peer->sin_addr, engine->peer_addr_str, peer_addrlen) == NULL) {
engine->peer_addr_str[0] = '\0';
}
} else {
if (inet_ntop(sa_peer->sin_family, &((struct sockaddr_in6*)sa_peer)->sin6_addr,
engine->peer_addr_str, peer_addrlen) == NULL)
{
engine->peer_addr_str[0] = '\0';
}
}
return engine->peer_addr_str;
}
char *
xqc_conn_addr_str(xqc_connection_t *conn)
{
if (conn->local_addrlen == 0 || conn->peer_addrlen == 0
|| conn->scid_set.user_scid.cid_len == 0 || conn->dcid_set.current_dcid.cid_len == 0)
{
return "addr or cid not avail";
}
if (conn->addr_str_len == 0) {
struct sockaddr_in *sa_local = (struct sockaddr_in *)conn->local_addr;
struct sockaddr_in *sa_peer = (struct sockaddr_in *)conn->peer_addr;
conn->addr_str_len = snprintf(conn->addr_str, sizeof(conn->addr_str), "l-%s-%d-%s p-%s-%d-%s",
xqc_local_addr_str(conn->engine, (struct sockaddr*)sa_local, conn->local_addrlen),
ntohs(sa_local->sin_port), xqc_scid_str(conn->engine, &conn->scid_set.user_scid),
xqc_peer_addr_str(conn->engine, (struct sockaddr*)sa_peer, conn->peer_addrlen),
ntohs(sa_peer->sin_port), xqc_dcid_str(conn->engine, &conn->dcid_set.current_dcid));
}
return conn->addr_str;
}
char *
xqc_path_addr_str(xqc_path_ctx_t *path)
{
if (path->local_addrlen == 0 || path->peer_addrlen == 0
|| path->path_scid.cid_len == 0 || path->path_dcid.cid_len == 0)
{
return "addr or cid not avail";
}
if (path->addr_str_len == 0) {
struct sockaddr_in *sa_local = (struct sockaddr_in *)path->local_addr;
struct sockaddr_in *sa_peer = (struct sockaddr_in *)path->peer_addr;
path->addr_str_len = snprintf(path->addr_str, sizeof(path->addr_str), "l-%s-%d-%s p-%s-%d-%s",
xqc_local_addr_str(path->parent_conn->engine, (struct sockaddr*)sa_local, path->local_addrlen),
ntohs(sa_local->sin_port), xqc_scid_str(path->parent_conn->engine, &path->path_scid),
xqc_peer_addr_str(path->parent_conn->engine, (struct sockaddr*)sa_peer, path->peer_addrlen),
ntohs(sa_peer->sin_port), xqc_dcid_str(path->parent_conn->engine, &path->path_dcid));
}
return path->addr_str;
}
void
xqc_conn_record_single(xqc_connection_t *c, xqc_packet_in_t *packet_in)
{
if (!xqc_has_packet_number(&packet_in->pi_pkt)) {
return;
}
xqc_path_ctx_t *path;
if (c->enable_multipath) {
path = xqc_conn_find_path_by_path_id(c, packet_in->pi_path_id);
} else {
path = c->conn_initial_path;
}
if (path == NULL) {
return;
}
/* update path stats */
if (packet_in->pi_frame_types & (XQC_FRAME_BIT_STREAM | XQC_FRAME_BIT_DATAGRAM)) {
path->path_send_ctl->ctl_app_bytes_recv += packet_in->buf_size;
}
xqc_pn_ctl_t *pn_ctl = xqc_get_pn_ctl(c, path);
xqc_send_ctl_t *send_ctl = path->path_send_ctl;
xqc_pkt_range_status range_status;
int out_of_order = 0;
xqc_pkt_num_space_t pns = packet_in->pi_pkt.pkt_pns;
xqc_packet_number_t pkt_num = packet_in->pi_pkt.pkt_num;
/* only recording the receive timestamp of pkt in app data space */
if ((c->conn_settings.extended_ack_features & XQC_ACK_EXT_FEATURE_BIT_RECV_TS)
&& pns == XQC_PNS_APP_DATA)
{
xqc_recv_timestamps_info_add_pkt(path->recv_ts_info, pkt_num, packet_in->pkt_recv_time);
}
range_status = xqc_recv_record_add(&pn_ctl->ctl_recv_record[pns], pkt_num);
if (range_status == XQC_PKTRANGE_OK) {
if (XQC_IS_ACK_ELICITING(packet_in->pi_frame_types)) {
++send_ctl->ctl_ack_eliciting_pkt[pns];
if (pkt_num > send_ctl->ctl_largest_received[pns] || send_ctl->ctl_largest_received[pns] == XQC_MAX_UINT64_VALUE) {
send_ctl->ctl_largest_received[pns] = pkt_num;
send_ctl->ctl_largest_recv_time[pns] = packet_in->pkt_recv_time;
}
}
if (pkt_num != xqc_recv_record_largest(&pn_ctl->ctl_recv_record[pns])) {
out_of_order = 1;
}
xqc_maybe_should_ack(c, path, pn_ctl, pns, out_of_order, packet_in->pkt_recv_time);
}
// xqc_recv_record_log(c, &pn_ctl->ctl_recv_record[pns]);
xqc_log(c->log, XQC_LOG_DEBUG, "|path:%ui|xqc_recv_record_add|status:%d|pkt_num:%ui|largest:%ui|pns:%d|",
path->path_id, range_status, pkt_num, xqc_recv_record_largest(&pn_ctl->ctl_recv_record[pns]), pns);
}
xqc_int_t
xqc_conn_confirm_cid(xqc_connection_t *c, xqc_packet_t *pkt)
{
/*
* after a successful process of Initial packet, SCID from Initial
* is not equal to what remembered when connection was created, as
* server is not willing to use the client's DCID as SCID;
*/
xqc_int_t ret;
if (!(c->conn_flag & XQC_CONN_FLAG_DCID_OK)) {
if (xqc_cid_in_cid_set(&c->dcid_set, &pkt->pkt_scid, XQC_INITIAL_PATH_ID) == NULL) {
ret = xqc_cid_set_insert_cid(&c->dcid_set, &pkt->pkt_scid, XQC_CID_USED,
c->local_settings.active_connection_id_limit,
XQC_INITIAL_PATH_ID);
if (ret != XQC_OK) {
xqc_log(c->log, XQC_LOG_ERROR,
"|xqc_cid_set_insert_cid error|limit:%ui|unused:%i|used:%i|",
c->local_settings.active_connection_id_limit,
xqc_cid_set_get_unused_cnt(&c->dcid_set, XQC_INITIAL_PATH_ID),
xqc_cid_set_get_used_cnt(&c->dcid_set, XQC_INITIAL_PATH_ID));
return ret;
}
}
if (XQC_OK != xqc_cid_is_equal(&c->dcid_set.current_dcid, &pkt->pkt_scid)) {
xqc_log(c->log, XQC_LOG_INFO, "|dcid change|ori:%s|new:%s|",
xqc_dcid_str(c->engine, &c->dcid_set.current_dcid), xqc_scid_str(c->engine, &pkt->pkt_scid));
xqc_cid_copy(&c->dcid_set.current_dcid, &pkt->pkt_scid);
xqc_cid_copy(&c->conn_initial_path->path_dcid, &pkt->pkt_scid);
xqc_datagram_record_mss(c);
}
if (xqc_insert_conns_hash(c->engine->conns_hash_dcid, c,
c->dcid_set.current_dcid.cid_buf,
c->dcid_set.current_dcid.cid_len))
{
xqc_log(c->log, XQC_LOG_ERROR, "|insert conn hash error");
return -XQC_EMALLOC;
}
c->conn_flag |= XQC_CONN_FLAG_DCID_OK;
}
return XQC_OK;
}
/**
* client will validate server's addr by:
* 1) successful processing of Initial packet.
* 2) successful processing of VN/Retry packet with the DCID client chose
* 3) server uses the CID which client provided in Initial packet with at least 8 bytes
*
* server will validate client's addr by:
* 1) successful processing of Handshake packet.
* 2) client's Initial/Handshake packet uses server's CID with at least 8 bytes
* 3) client's Initial token is what server provided in NEW_TOKEN/Retry frame
*/
void
xqc_conn_addr_validated(xqc_connection_t *c)
{
c->conn_flag |= XQC_CONN_FLAG_ADDR_VALIDATED;
xqc_log(c->log, XQC_LOG_INFO, "|Address Validated|conn:%p|role:%d|", c, c->conn_type);
}
void
xqc_conn_server_validate_address(xqc_connection_t *c, xqc_packet_in_t *pi)
{
switch (pi->pi_pkt.pkt_type) {
case XQC_PTYPE_INIT:
if (XQC_CONN_FLAG_TOKEN_OK & c->conn_flag) {
/* NEW_TOKEN or Retry token is valid */
xqc_conn_addr_validated(c);
} else {
/**
* when server close its own CID, and server reached its anti-amplification limit,
* client MAY send an Initial packet with PING/PADDING on PTO with server's CID
*/
if (c->scid_set.user_scid.cid_len >= XQC_CONN_ADDR_VALIDATION_CID_ENTROPY
&& xqc_cid_in_cid_set(&c->scid_set, &c->original_dcid, XQC_INITIAL_PATH_ID) == NULL
&& xqc_cid_in_cid_set(&c->scid_set, &pi->pi_pkt.pkt_dcid, XQC_INITIAL_PATH_ID) != NULL)
{
xqc_conn_addr_validated(c);
}
}
break;
case XQC_PTYPE_HSK:
/* successful processing of Handshake packet */
xqc_conn_addr_validated(c);
break;
default:
break;
}
/*
* loss detection timer might be unset when anti-amplification limit is reached, but receiving
* a handshake or receiving a packet with cid which was choosed by server, will remove the
* anti-amplification state, and loss detection timer shall be re-armed.
*/
if (c->conn_flag & XQC_CONN_FLAG_ADDR_VALIDATED) {
xqc_send_ctl_rearm_ld_timer(c->conn_initial_path->path_send_ctl);
}
}
void
xqc_conn_client_validate_address(xqc_connection_t *c, xqc_packet_in_t *pi)
{
switch (pi->pi_pkt.pkt_type) {
case XQC_PTYPE_INIT:
case XQC_PTYPE_RETRY:
case XQC_PTYPE_VERSION_NEGOTIATION:
xqc_conn_addr_validated(c);
break;
default:
break;
}
}
void
xqc_conn_validate_address(xqc_connection_t *c, xqc_packet_in_t *pi)
{
if (c->conn_type == XQC_CONN_TYPE_SERVER) {
xqc_conn_server_validate_address(c, pi);
} else {
xqc_conn_client_validate_address(c, pi);
}
}
xqc_int_t
xqc_conn_on_initial_processed(xqc_connection_t *c, xqc_packet_in_t *pi, xqc_usec_t now)
{
/* successful decryption of initial packet means that pkt's DCID/SCID is confirmed */
return xqc_conn_confirm_cid(c, &pi->pi_pkt);
}
xqc_int_t
xqc_conn_on_hsk_processed(xqc_connection_t *c, xqc_packet_in_t *pi, xqc_usec_t now)
{
if (c->conn_hsk_recv_time == 0) {
c->conn_hsk_recv_time = now;
}
if (c->conn_type == XQC_CONN_TYPE_SERVER) {
/* server MUST discard Initial keys when it first successfully processes a Handshake packet */
xqc_send_queue_drop_initial_packets(c);
}
return XQC_OK;
}
xqc_int_t
xqc_conn_on_1rtt_processed(xqc_connection_t *c, xqc_packet_in_t *pi, xqc_usec_t now)
{
if (c->conn_type == XQC_CONN_TYPE_CLIENT) {
/*
* once client receives HANDSHAKE_DONE frame, handshake
* is confirmed, and MUST discard its handshake keys
*/
if (pi->pi_frame_types & XQC_FRAME_BIT_HANDSHAKE_DONE) {
xqc_conn_handshake_confirmed(c);
}
}
return XQC_OK;
}
xqc_int_t
xqc_conn_on_pkt_processed(xqc_connection_t *c, xqc_packet_in_t *pi, xqc_usec_t now)
{
xqc_int_t ret = XQC_OK;
switch (pi->pi_pkt.pkt_type) {
case XQC_PTYPE_INIT:
ret = xqc_conn_on_initial_processed(c, pi, now);
break;
case XQC_PTYPE_HSK:
ret = xqc_conn_on_hsk_processed(c, pi, now);
break;
case XQC_PTYPE_SHORT_HEADER:
ret = xqc_conn_on_1rtt_processed(c, pi, now);
break;
default:
break;
}
/* validate peer's address */
if (!(c->conn_flag & XQC_CONN_FLAG_ADDR_VALIDATED)) {
xqc_conn_validate_address(c, pi);
}
/* record packet */
xqc_conn_record_single(c, pi);
if (pi->pi_frame_types & (~(XQC_FRAME_BIT_STREAM|XQC_FRAME_BIT_DATAGRAM|XQC_FRAME_BIT_PADDING|XQC_FRAME_BIT_SID|XQC_FRAME_BIT_REPAIR_SYMBOL))) {
c->conn_flag |= XQC_CONN_FLAG_NEED_RUN;
}
c->conn_last_recv_time = now;
xqc_log(c->log, XQC_LOG_INFO, "|====>|conn:%p|path:%ui|size:%uz|pkt_type:%s|pkt_num:%ui|frame:%s|recv_time:%ui|dcid:%s|dcid_seq:%ui|",
c, pi->pi_path_id, pi->buf_size, xqc_pkt_type_2_str(pi->pi_pkt.pkt_type), pi->pi_pkt.pkt_num,
xqc_frame_type_2_str(c->engine, pi->pi_frame_types), pi->pkt_recv_time,
xqc_dcid_str(c->engine, &pi->pi_pkt.pkt_dcid),
pi->pi_pkt.pkt_dcid.cid_seq_num);
return ret;
}
uint8_t
xqc_conn_tolerant_error(xqc_int_t ret)
{
if (-XQC_EVERSION == ret || -XQC_EILLPKT == ret || -XQC_EWAITING == ret
|| -XQC_EIGNORE_PKT == ret || -XQC_EDECRYPT == ret)
{
return XQC_TRUE;
}
return XQC_FALSE;
}
static inline void
xqc_conn_log_recvd_packet(xqc_connection_t *c, xqc_packet_in_t *pi,
size_t udp_size, xqc_int_t err, xqc_usec_t timestamp)
{
int index = c->rcv_pkt_stats.curr_index;
c->rcv_pkt_stats.pkt_frames[index] = pi->pi_frame_types;
c->rcv_pkt_stats.pkt_err[index] = err;
c->rcv_pkt_stats.pkt_size[index] = pi->pi_pkt.length;
c->rcv_pkt_stats.pkt_timestamp[index] = xqc_calc_delay(timestamp,
c->conn_create_time);
c->rcv_pkt_stats.pkt_timestamp[index] /= 1000; // ms
c->rcv_pkt_stats.pkt_udp_size[index] = udp_size;
c->rcv_pkt_stats.pkt_types[index] = pi->pi_pkt.pkt_type;
c->rcv_pkt_stats.pkt_pn[index] = pi->pi_pkt.pkt_num;
c->rcv_pkt_stats.conn_rcvd_pkts++;
c->rcv_pkt_stats.curr_index = (index + 1) % 3;
}
xqc_int_t
xqc_conn_process_packet(xqc_connection_t *c,
const unsigned char *packet_in_buf, size_t packet_in_size,
xqc_usec_t recv_time)
{
xqc_int_t ret = XQC_OK;
const unsigned char *last_pos = NULL;
const unsigned char *pos = packet_in_buf; /* start of QUIC pkt */
const unsigned char *end = packet_in_buf + packet_in_size; /* end of udp datagram */
xqc_packet_in_t packet;
unsigned char decrypt_payload[XQC_MAX_PACKET_IN_LEN];
/* process all QUIC packets in UDP datagram */
while (pos < end) {
last_pos = pos;
/* init packet in */
xqc_packet_in_t *packet_in = &packet;
memset(packet_in, 0, sizeof(*packet_in));
xqc_packet_in_init(packet_in, pos, end - pos, decrypt_payload, XQC_MAX_PACKET_IN_LEN, recv_time);
packet_in->pi_path_id = XQC_UNKNOWN_PATH_ID;
/* packet_in->pos will update inside */
ret = xqc_packet_process_single(c, packet_in);
xqc_conn_log_recvd_packet(c, packet_in, packet_in_size, ret, recv_time);
if (ret == XQC_OK) {
ret = xqc_conn_on_pkt_processed(c, packet_in, recv_time);
} else if (xqc_conn_tolerant_error(ret)) {
/* ignore the remain bytes */
xqc_log(c->log, XQC_LOG_INFO, "|ignore err|%d|", ret);
packet_in->pos = packet_in->last;
ret = XQC_OK;
goto end;
}
/* error occurred or read state is error */
if (ret != XQC_OK || last_pos == packet_in->pos) {
/* if last_pos equals packet_in->pos, might trigger infinite loop, return to avoid it */
xqc_log(c->log, XQC_LOG_ERROR, "|process packets err|ret:%d|pos:%p|buf:%p|buf_size:%uz|",
ret, packet_in->pos, packet_in->buf, packet_in->buf_size);
return ret != XQC_OK ? ret : -XQC_ESYS;
}
/* consume all the bytes and start parse next QUIC packet */
pos = packet_in->last;
xqc_log_event(c->log, TRA_PACKET_RECEIVED, packet_in);
}
end:
return ret;
}
void
xqc_conn_process_packet_recved_path(xqc_connection_t *conn, xqc_cid_t *scid,
size_t packet_in_size, xqc_usec_t recv_time)
{
xqc_path_ctx_t *path = NULL;
if (conn->enable_multipath) {
path = xqc_conn_find_path_by_scid(conn, scid);
} else {
path = conn->conn_initial_path;
}
if (path == NULL) {
xqc_log(conn->log, XQC_LOG_INFO, "|ignore unknown path|scid:%s|", xqc_scid_str(conn->engine, scid));
return;
}
xqc_send_ctl_on_dgram_received(path->path_send_ctl, packet_in_size);
xqc_timer_set(&path->path_send_ctl->path_timer_manager, XQC_TIMER_PATH_IDLE,
recv_time, xqc_path_get_idle_timeout(path) * 1000);
return;
}
xqc_int_t
xqc_conn_check_tx_key(xqc_connection_t *conn)
{
/* if tx key is ready, conn can send 1RTT packets */
if (!(conn->conn_flag & XQC_CONN_FLAG_CAN_SEND_1RTT)
&& xqc_tls_is_key_ready(conn->tls, XQC_ENC_LEV_1RTT, XQC_KEY_TYPE_TX_WRITE)) {
xqc_log(conn->log, XQC_LOG_INFO, "|keys are ready, can send 1rtt now|");
conn->conn_flag |= XQC_CONN_FLAG_CAN_SEND_1RTT;
xqc_datagram_record_mss(conn);
}
return XQC_OK;
}
xqc_int_t
xqc_conn_check_handshake_complete(xqc_connection_t *conn)
{
/* check tx keys after handshake complete */
xqc_conn_check_tx_key(conn);
if (!(conn->conn_flag & XQC_CONN_FLAG_HANDSHAKE_COMPLETED)
&& conn->conn_state == XQC_CONN_STATE_ESTABED)
{
xqc_log(conn->log, XQC_LOG_DEBUG, "|HANDSHAKE_COMPLETED|conn:%p|", conn);
xqc_conn_handshake_complete(conn);
if (conn->app_proto_cbs.conn_cbs.conn_handshake_finished) {
conn->app_proto_cbs.conn_cbs.conn_handshake_finished(conn, conn->user_data, conn->proto_data);
}
}
return XQC_OK;
}
xqc_int_t
xqc_conn_get_available_path_id(xqc_connection_t *conn, uint64_t *path_id)
{
if (conn->enable_multipath
&& conn->conn_settings.multipath_version >= XQC_MULTIPATH_10)
{
/* principle: the next unused path ID has at least one unused DCID and one acked unused SCID */
xqc_cid_set_inner_t *scid_inner_set = xqc_get_next_unused_path_cid_set(&conn->scid_set);
if (scid_inner_set) {
xqc_cid_set_inner_t *dcid_inner_set = xqc_get_path_cid_set(&conn->dcid_set, scid_inner_set->path_id);
if (dcid_inner_set) {
if (dcid_inner_set->unused_cnt > 0 && scid_inner_set->acked_unused > 0) {
if (path_id) {
*path_id = scid_inner_set->path_id;
}
return XQC_OK;
}
}
}
}
return -XQC_EMP_NO_AVAIL_PATH_ID;
}
void
xqc_conn_destroy_cids(xqc_connection_t *conn)
{
xqc_cid_inner_t *cid = NULL;
xqc_list_head_t *pos, *next;
xqc_cid_set_inner_t *inner_set = NULL;
xqc_list_head_t *pos_set, *next_set;
if (conn->engine->conns_hash) {
if (xqc_find_conns_hash(conn->engine->conns_hash, conn,
conn->original_dcid.cid_buf,
conn->original_dcid.cid_len))
{
xqc_remove_conns_hash(conn->engine->conns_hash, conn,
conn->original_dcid.cid_buf,
conn->original_dcid.cid_len);
}
xqc_list_for_each_safe(pos_set, next_set, &conn->scid_set.cid_set_list) {
inner_set = xqc_list_entry(pos_set, xqc_cid_set_inner_t, next);
xqc_list_for_each_safe(pos, next, &inner_set->cid_list) {
cid = xqc_list_entry(pos, xqc_cid_inner_t, list);
if (xqc_find_conns_hash(conn->engine->conns_hash, conn,
cid->cid.cid_buf, cid->cid.cid_len))
{
xqc_remove_conns_hash(conn->engine->conns_hash, conn,
cid->cid.cid_buf, cid->cid.cid_len);
}
}
}
}
xqc_list_for_each_safe(pos_set, next_set, &conn->dcid_set.cid_set_list) {
inner_set = xqc_list_entry(pos_set, xqc_cid_set_inner_t, next);
xqc_list_for_each_safe(pos, next, &inner_set->cid_list) {
cid = xqc_list_entry(pos, xqc_cid_inner_t, list);
if (conn->engine->conns_hash_dcid) {
/* delete relationship from conns_hash_dcid */
if (xqc_find_conns_hash(conn->engine->conns_hash_dcid, conn,
cid->cid.cid_buf, cid->cid.cid_len))
{
xqc_remove_conns_hash(conn->engine->conns_hash_dcid, conn,
cid->cid.cid_buf, cid->cid.cid_len);
}
}
if (conn->engine->conns_hash_sr_token) {
/* delete relationship from conns_hash_sr_token */
if (xqc_find_conns_hash(conn->engine->conns_hash_sr_token, conn,
cid->cid.sr_token,
XQC_STATELESS_RESET_TOKENLEN))
{
xqc_remove_conns_hash(conn->engine->conns_hash_sr_token, conn,
cid->cid.sr_token,
XQC_STATELESS_RESET_TOKENLEN);
}
}
}
}
xqc_destroy_cid_set(&conn->scid_set);
xqc_destroy_cid_set(&conn->dcid_set);
}
xqc_int_t
xqc_conn_try_add_new_conn_id(xqc_connection_t *conn, uint64_t retire_prior_to)
{
xqc_int_t ret = XQC_OK;
uint8_t unused_limit = 2;
xqc_cid_set_inner_t *inner_set;
if (xqc_conn_is_handshake_confirmed(conn)) {
if (conn->enable_multipath && conn->conn_settings.multipath_version >= XQC_MULTIPATH_10)
{
/* principle #1 there are two CIDs for the next path ID */
inner_set = xqc_get_next_unused_path_cid_set(&conn->scid_set);
while (inner_set
&& (inner_set->unused_cnt + inner_set->used_cnt) < conn->remote_settings.active_connection_id_limit
&& inner_set->unused_cnt < unused_limit)
{
ret = xqc_write_mp_new_conn_id_frame_to_packet(conn, retire_prior_to, inner_set->path_id);
if (ret != XQC_OK) {
xqc_log(conn->log, XQC_LOG_ERROR,
"|xqc_write_mp_new_conn_id_frame_to_packet error|path_id:%ui|",
inner_set->path_id);
return ret;
}
}
/* principle #2 there are two CIDs for every in-use path ID */
xqc_list_head_t *pos, *next;
xqc_list_for_each_safe(pos, next, &conn->scid_set.cid_set_list) {
inner_set = xqc_list_entry(pos, xqc_cid_set_inner_t, next);
if (inner_set->set_state == XQC_CID_SET_USED) {
while (inner_set
&& (inner_set->unused_cnt + inner_set->used_cnt) < conn->remote_settings.active_connection_id_limit
&& inner_set->unused_cnt < unused_limit)
{
ret = xqc_write_mp_new_conn_id_frame_to_packet(conn, retire_prior_to, inner_set->path_id);
if (ret != XQC_OK) {
xqc_log(conn->log, XQC_LOG_ERROR,
"|xqc_write_mp_new_conn_id_frame_to_packet error|path_id:%ui|",
inner_set->path_id);
return ret;
}
}
}
}
} else {
inner_set = xqc_get_path_cid_set(&conn->scid_set, XQC_INITIAL_PATH_ID);
/* origin logic for new connection id */
while ((inner_set->used_cnt + inner_set->unused_cnt) < conn->remote_settings.active_connection_id_limit
&& inner_set->unused_cnt < unused_limit)
{
ret = xqc_write_new_conn_id_frame_to_packet(conn, retire_prior_to);
if (ret != XQC_OK) {
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_conn_id_frame_to_packet error|");
return ret;
}
}
}
}
return XQC_OK;
}
void
xqc_conn_ptmud_probing(xqc_connection_t *conn)
{
if (conn->conn_state >= XQC_CONN_STATE_CLOSING) {
xqc_log(conn->log, XQC_LOG_INFO, "|conn closing, cannot send PMTUD probing|");
}
/* probing can only be sent in 0RTT/1RTT packets */
xqc_pkt_type_t pkt_type = XQC_PTYPE_SHORT_HEADER;
int support_0rtt = xqc_conn_is_ready_to_send_early_data(conn);
if (!(conn->conn_flag & XQC_CONN_FLAG_CAN_SEND_1RTT)) {
if ((conn->conn_type == XQC_CONN_TYPE_CLIENT)
&& (conn->conn_state == XQC_CONN_STATE_CLIENT_INITIAL_SENT)
&& support_0rtt)
{
pkt_type = XQC_PTYPE_0RTT;
conn->conn_flag |= XQC_CONN_FLAG_HAS_0RTT;
} else {
return;
}
}
if (pkt_type == XQC_PTYPE_0RTT
&& conn->zero_rtt_count >= XQC_PACKET_0RTT_MAX_COUNT)
{
return;
}
/* generate PING packets */
if (conn->probing_cnt >= 3) {
/* if the current MSS has been already probed for 3 times
* while the MSS is not updated, we need to shrink the probing size
*/
conn->max_pkt_out_size = xqc_max(conn->probing_pkt_out_size - 1, conn->pkt_out_size);
conn->probing_pkt_out_size = xqc_max(conn->pkt_out_size, (conn->max_pkt_out_size + conn->pkt_out_size) >> 1);
conn->probing_cnt = 0;
}
/* stop probing if the range is less than 10B */
if ((conn->max_pkt_out_size - conn->pkt_out_size) < 10) {
xqc_log_event(conn->log, CON_MTU_UPDATED, conn, 1);
conn->conn_flag &= ~XQC_CONN_FLAG_PMTUD_PROBING;
return;
}
xqc_log_event(conn->log, CON_MTU_UPDATED, conn, 0);
conn->MTU_updated_count ++;
size_t probing_size = conn->probing_pkt_out_size;
xqc_list_head_t *pos, *next;
xqc_path_ctx_t *path;
xqc_int_t ret = XQC_OK;
xqc_usec_t probing_interval = conn->conn_settings.pmtud_probing_interval;
/* only probing on active paths */
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
if (path->path_state != XQC_PATH_STATE_ACTIVE) {
continue;
}
ret = xqc_write_pmtud_ping_to_packet(path, probing_size, pkt_type);
if (ret < 0) {
xqc_log(conn->log, XQC_LOG_ERROR, "|genrate PMTUD ping packet error|ret:%d|", ret);
}
}
/* set timer: default 500ms, 1000ms, or 2000ms according to probing_cnt */
xqc_timer_set(&conn->conn_timer_manager,
XQC_TIMER_PMTUD_PROBING,
xqc_monotonic_timestamp(),
probing_interval * (1 << conn->probing_cnt));
conn->probing_cnt++;
conn->conn_flag &= ~XQC_CONN_FLAG_PMTUD_PROBING;
}
xqc_int_t
xqc_conn_confirm_key_update(xqc_connection_t *conn)
{
xqc_key_update_ctx_t *ctx = &conn->key_update_ctx;
xqc_pn_ctl_t *pn_ctl = xqc_get_pn_ctl(conn, conn->conn_initial_path);
ctx->key_update_cnt++;
ctx->first_sent_pktno = pn_ctl->ctl_packet_number[XQC_PNS_APP_DATA] + 1;
ctx->first_recv_pktno = XQC_MAX_UINT64_VALUE;
ctx->cur_out_key_phase ^= 1;
ctx->next_in_key_phase ^= 1;
ctx->enc_pkt_cnt = 0;
xqc_tls_set_1rtt_key_phase(conn->tls, ctx->cur_out_key_phase);
xqc_log(conn->log, XQC_LOG_DEBUG, "|key phase changed to %ui|", ctx->cur_out_key_phase);
/*
* An endpoint SHOULD retain old read keys for no more than three times the PTO after having
* received a packet protected using the new keys. After this period, old read keys and their
* corresponding secrets SHOULD be discarded.
*/
xqc_usec_t now = xqc_monotonic_timestamp();
xqc_usec_t pto = xqc_conn_get_max_pto(conn);
if (!xqc_timer_is_set(&conn->conn_timer_manager, XQC_TIMER_KEY_UPDATE)) {
xqc_timer_set(&conn->conn_timer_manager, XQC_TIMER_KEY_UPDATE, now, 3 * pto);
}
/*
* Endpoints need to allow for the possibility that a peer might not be able to decrypt
* packets that initiate a key update during the period when the peer retains old keys.
* Endpoints SHOULD wait three times the PTO before initiating a key update after receiving
* an acknowledgment that confirms that the previous key update was received.
* Failing to allow sufficient time could lead to packets being discarded.
*/
ctx->initiate_time_guard = now + 3 * pto;
return XQC_OK;
}
/* check whether if the dcid is valid for the connection */
xqc_int_t
xqc_conn_check_dcid(xqc_connection_t *conn, xqc_cid_t *dcid)
{
xqc_int_t ret;
xqc_cid_inner_t *scid = xqc_cid_set_search_cid(&conn->scid_set, dcid);
if (scid == NULL) {
return -XQC_ECONN_CID_NOT_FOUND;
}
if (scid->state == XQC_CID_UNUSED) {
ret = xqc_cid_switch_to_next_state(&conn->scid_set, scid, XQC_CID_USED, scid->cid.path_id);
if (ret < 0) {
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_cid_switch_to_next_state error|scid:%s|",
xqc_scid_str(conn->engine, &scid->cid));
return ret;
}
}
return XQC_OK;
}
xqc_int_t
xqc_conn_set_cid_retired_ts(xqc_connection_t *conn, xqc_cid_inner_t *inner_cid)
{
xqc_int_t ret = XQC_OK;
ret = xqc_cid_switch_to_next_state(&conn->scid_set, inner_cid, XQC_CID_RETIRED, inner_cid->cid.path_id);
if (ret != XQC_OK) {
xqc_log(conn->log, XQC_LOG_ERROR, "|set cid retired error|");
return ret;
}
xqc_log(conn->log, XQC_LOG_DEBUG,
"|retired|cid:%s|seq:%ui|len:%d|",
xqc_scid_str(conn->engine, &inner_cid->cid),
inner_cid->cid.cid_seq_num,
inner_cid->cid.cid_len);
xqc_usec_t now = xqc_monotonic_timestamp();
xqc_usec_t pto = xqc_conn_get_max_pto(conn);
/* set retired timestamp */
inner_cid->retired_ts = now + 3 * pto;
/* set timer to remove the retired cids */
if (!xqc_timer_is_set(&conn->conn_timer_manager, XQC_TIMER_RETIRE_CID)) {
xqc_timer_set(&conn->conn_timer_manager, XQC_TIMER_RETIRE_CID, now, 3 * pto);
}
return ret;
}
/* switch another used scid to replace user_scid */
xqc_int_t
xqc_conn_update_user_scid(xqc_connection_t *conn)
{
xqc_cid_inner_t *scid;
xqc_list_head_t *pos, *next;
xqc_cid_set_inner_t *inner_set;
xqc_list_head_t *pos_set, *next_set;
xqc_list_for_each_safe(pos_set, next_set, &conn->scid_set.cid_set_list) {
inner_set = xqc_list_entry(pos_set, xqc_cid_set_inner_t, next);
xqc_list_for_each_safe(pos, next, &inner_set->cid_list) {
scid = xqc_list_entry(pos, xqc_cid_inner_t, list);
if (scid->state == XQC_CID_USED
&& xqc_cid_is_equal(&conn->scid_set.user_scid, &scid->cid) != XQC_OK)
{
if (conn->transport_cbs.conn_update_cid_notify) {
conn->transport_cbs.conn_update_cid_notify(conn, &conn->scid_set.user_scid, &scid->cid,
xqc_conn_get_user_data(conn));
}
xqc_cid_copy(&conn->scid_set.user_scid, &scid->cid);
xqc_datagram_record_mss(conn);
return XQC_OK;
}
}
}
return -XQC_ECONN_NO_AVAIL_CID;
}
xqc_bool_t
xqc_conn_peer_complete_address_validation(xqc_connection_t *c)
{
/* server assume clients validate server's address implicitly */
if (c->conn_type == XQC_CONN_TYPE_SERVER) {
return XQC_TRUE;
} else {
return (c->conn_flag & XQC_CONN_FLAG_HANDSHAKE_CONFIRMED)
|| xqc_send_ctl_ack_received_in_pns(c->conn_initial_path->path_send_ctl, XQC_PNS_HSK);
}
}
void *
xqc_conn_get_user_data(xqc_connection_t *c)
{
if (NULL == c) {
return NULL;
}
return c->user_data;
}
xqc_bool_t
xqc_conn_has_hsk_keys(xqc_connection_t *c)
{
return xqc_tls_is_key_ready(c->tls, XQC_ENC_LEV_HSK, XQC_KEY_TYPE_TX_WRITE)
&& xqc_tls_is_key_ready(c->tls, XQC_ENC_LEV_HSK, XQC_KEY_TYPE_RX_READ);
}
static xqc_bool_t
xqc_need_reassemble_packet(xqc_packet_out_t *packet_out)
{
if (packet_out->po_pkt.pkt_type == XQC_PTYPE_INIT
&& packet_out->po_frame_types & XQC_FRAME_BIT_CRYPTO)
{
return XQC_TRUE;
} else if (packet_out->po_pkt.pkt_type == XQC_PTYPE_0RTT) {
return XQC_TRUE;
}
return XQC_FALSE;
}
static xqc_int_t
xqc_conn_reassemble_packet(xqc_connection_t *conn, xqc_packet_out_t *ori_po)
{
xqc_packet_out_t *new_po = xqc_write_new_packet(conn, ori_po->po_pkt.pkt_type);
if (new_po == NULL) {
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|");
return -XQC_EWRITE_PKT;
}
/* copy frame without padding */
unsigned int ori_payload_len = 0;
if (xqc_need_padding(conn, ori_po) && ori_po->po_padding != NULL) {
ori_payload_len = ori_po->po_padding - ori_po->po_payload;
} else {
ori_payload_len = ori_po->po_used_size - (ori_po->po_payload - ori_po->po_buf);
}
new_po->po_payload = new_po->po_buf + new_po->po_used_size;
memcpy(new_po->po_payload, ori_po->po_payload, ori_payload_len);
new_po->po_used_size += ori_payload_len;
/* copy packet_out info */
new_po->po_frame_types = ori_po->po_frame_types;
for (int i = 0; i < XQC_MAX_STREAM_FRAME_IN_PO; i++) {
new_po->po_stream_frames[i] = ori_po->po_stream_frames[i];
}
/* set RESEND flag */
new_po->po_flag |= XQC_POF_RESEND;
if (new_po->po_frame_types & XQC_FRAME_BIT_CRYPTO) {
xqc_send_queue_move_to_high_pri(&new_po->po_list, conn->conn_send_queue);
}
xqc_log(conn->log, XQC_LOG_DEBUG,
"|pkt_num:%ui|ptype:%d|frames:%s|",
new_po->po_pkt.pkt_num, new_po->po_pkt.pkt_type,
xqc_frame_type_2_str(conn->engine, new_po->po_frame_types));
return XQC_OK;
}
static xqc_int_t
xqc_conn_resend_packets(xqc_connection_t *conn)
{
/*
* Generate new header and reassemble packet for Initial and 0-RTT packets
* that need to be resent, and drop all old packets with the original header.
*
* TODO: Refactoring packet generation: generate packet header before sent.
* Then we don't have to reassemble the packets.
*/
xqc_int_t ret;
xqc_send_queue_t *send_queue = conn->conn_send_queue;
xqc_list_head_t *pos, *next;
xqc_packet_out_t *packet_out;
xqc_list_for_each_safe(pos, next, &send_queue->sndq_send_packets) {
packet_out = xqc_list_entry(pos, xqc_packet_out_t, po_list);
if (packet_out->po_flag & XQC_POF_RESEND) {
continue;
}
/* reassemble new packet with updated header and insert to send queue */
if (xqc_need_reassemble_packet(packet_out)) {
ret = xqc_conn_reassemble_packet(conn, packet_out);
if (ret != XQC_OK) {
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_conn_reassemble_packet error|ret:%d|", ret);
return ret;
}
}
/* drop old packet */
xqc_send_queue_remove_send(pos);
xqc_send_queue_insert_free(packet_out, &send_queue->sndq_free_packets, send_queue);
}
return XQC_OK;
}
xqc_int_t
xqc_conn_on_recv_retry(xqc_connection_t *conn, xqc_cid_t *retry_scid)
{
xqc_int_t ret;
conn->conn_flag |= XQC_CONN_FLAG_RETRY_RECVD;
/* change the DCID it uses for sending packets in response to Retry packet. */
xqc_cid_copy(&conn->dcid_set.current_dcid, retry_scid);
xqc_datagram_record_mss(conn);
/* reset initial keys */
ret = xqc_tls_reset_initial(conn->tls, conn->version, retry_scid);
if (ret != XQC_OK) {
xqc_log(conn->log, XQC_LOG_ERROR,
"|xqc_tls_reset_initial error|retry_scid:%s|ret:%d|",
xqc_scid_str(conn->engine, retry_scid), ret);
return ret;
}
/*
* clients that receive a Retry packet reset congestion control and loss
* recovery state, including resetting any pending timers.
*/
xqc_send_ctl_reset(conn->conn_initial_path->path_send_ctl);
/*
* client responds to a Retry packet with an Initial packet that includes
* the provided Retry token to continue connection establishment.
* client SHOULD attempt to resend data in 0-RTT packets after it sends a
* new Initial packet.
*/
ret = xqc_conn_resend_packets(conn);
if (ret != XQC_OK) {
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_conn_resend_packets error|ret:%d|", ret);
return ret;
}
return XQC_OK;
}
xqc_int_t
xqc_conn_set_remote_transport_params(xqc_connection_t *conn,
const xqc_transport_params_t *params, xqc_transport_params_type_t exttype)
{
/* check transport parameters type */
switch (exttype) {
case XQC_TP_TYPE_CLIENT_HELLO:
if (conn->conn_type != XQC_CONN_TYPE_SERVER) {
xqc_log(conn->log, XQC_LOG_ERROR, "|wrong transport_param_type|CH|");
return -XQC_EPARAM;
}
break;
case XQC_TP_TYPE_ENCRYPTED_EXTENSIONS:
if (conn->conn_type != XQC_CONN_TYPE_CLIENT) {
xqc_log(conn->log, XQC_LOG_ERROR, "|wrong transport_param_type|EE|");
return -XQC_EPARAM;
}
break;
default:
xqc_log(conn->log, XQC_LOG_ERROR, "|unknown transport_param_type|");
return -XQC_EPARAM;
}
/* copy settings from transport parameters */
xqc_trans_settings_t *settings = &conn->remote_settings;
settings->max_stream_data_bidi_local = params->initial_max_stream_data_bidi_local;
settings->max_stream_data_bidi_remote = params->initial_max_stream_data_bidi_remote;
settings->max_stream_data_uni = params->initial_max_stream_data_uni;
settings->max_data = params->initial_max_data;
settings->max_streams_bidi = params->initial_max_streams_bidi;
settings->max_streams_uni = params->initial_max_streams_uni;
settings->max_idle_timeout = params->max_idle_timeout;
settings->max_udp_payload_size = params->max_udp_payload_size;
settings->stateless_reset_token_present = params->stateless_reset_token_present;
if (params->stateless_reset_token_present) {
xqc_memcpy(settings->stateless_reset_token, params->stateless_reset_token,
sizeof(settings->stateless_reset_token));
} else {
xqc_memset(settings->stateless_reset_token, 0, sizeof(settings->stateless_reset_token));
}
settings->ack_delay_exponent = params->ack_delay_exponent;
settings->disable_active_migration = params->disable_active_migration;
settings->max_ack_delay = params->max_ack_delay;
settings->preferred_address = params->preferred_address;
settings->active_connection_id_limit = params->active_connection_id_limit;
settings->enable_multipath = params->enable_multipath;
settings->multipath_version = params->multipath_version;
settings->init_max_path_id = params->init_max_path_id;
settings->max_datagram_frame_size = params->max_datagram_frame_size;
settings->close_dgram_redundancy = params->close_dgram_redundancy;
settings->enable_pmtud = params->enable_pmtud;
#ifdef XQC_ENABLE_FEC
/*
* set fec params to remote_settings
*/
settings->fec_version = params->fec_version;
if (params->fec_version != XQC_ERR_FEC_VERSION) {
// if current host enable fec encode, set decoder params of remote settings
if (conn->conn_settings.enable_encode_fec) {
settings->enable_decode_fec = params->enable_decode_fec;
settings->fec_decoder_schemes_num = params->fec_decoder_schemes_num;
for (xqc_int_t i = 0; i < settings->fec_decoder_schemes_num; i++) {
settings->fec_decoder_schemes[i] = params->fec_decoder_schemes[i];
}
}
// if current host enable fec decode, set encoder params of remote settings
if (conn->conn_settings.enable_decode_fec) {
settings->enable_encode_fec = params->enable_encode_fec;
settings->fec_max_symbols_num = params->fec_max_symbols_num;
settings->fec_encoder_schemes_num = params->fec_encoder_schemes_num;
for (xqc_int_t i = 0; i < settings->fec_encoder_schemes_num; i++) {
settings->fec_encoder_schemes[i] = params->fec_encoder_schemes[i];
}
}
} else {
settings->enable_encode_fec = 0;
settings->enable_decode_fec = 0;
}
#endif
settings->extended_ack_features = params->extended_ack_features;
settings->max_receive_timestamps_per_ack = params->max_receive_timestamps_per_ack;
settings->receive_timestamps_exponent = params->receive_timestamps_exponent;
if (conn->conn_type == XQC_CONN_TYPE_SERVER
&& settings->max_udp_payload_size >= XQC_PACKET_OUT_SIZE) {
conn->pkt_out_size = xqc_min(conn->pkt_out_size, settings->max_udp_payload_size - XQC_PACKET_OUT_EXT_SPACE);
}
return XQC_OK;
}
xqc_int_t
xqc_conn_get_local_transport_params(xqc_connection_t *conn, xqc_transport_params_t *params)
{
/* copy transport parameters from conn local settings */
xqc_trans_settings_t *settings = &conn->local_settings;
params->initial_max_stream_data_bidi_local = settings->max_stream_data_bidi_local;
params->initial_max_stream_data_bidi_remote = settings->max_stream_data_bidi_remote;
params->initial_max_stream_data_uni = settings->max_stream_data_uni;
params->initial_max_data = settings->max_data;
params->initial_max_streams_bidi = settings->max_streams_bidi;
params->initial_max_streams_uni = settings->max_streams_uni;
params->max_idle_timeout = settings->max_idle_timeout;
params->max_udp_payload_size = settings->max_udp_payload_size;
params->stateless_reset_token_present = settings->stateless_reset_token_present;
if (settings->stateless_reset_token_present) {
memcpy(params->stateless_reset_token, settings->stateless_reset_token,
sizeof(params->stateless_reset_token));
} else {
memset(params->stateless_reset_token, 0, sizeof(params->stateless_reset_token));
}
params->ack_delay_exponent = settings->ack_delay_exponent;
params->disable_active_migration = settings->disable_active_migration;
params->max_ack_delay = settings->max_ack_delay;
params->preferred_address = settings->preferred_address;
params->active_connection_id_limit = settings->active_connection_id_limit;
params->no_crypto = settings->no_crypto;
params->enable_multipath = settings->enable_multipath;
params->multipath_version = settings->multipath_version;
params->init_max_path_id = settings->init_max_path_id;
params->max_datagram_frame_size = settings->max_datagram_frame_size;
params->enable_pmtud = settings->enable_pmtud;
params->close_dgram_redundancy = settings->close_dgram_redundancy;
#ifdef XQC_ENABLE_FEC
if (conn->conn_settings.enable_encode_fec) {
params->enable_encode_fec = settings->enable_encode_fec;
params->fec_max_symbols_num = settings->fec_max_symbols_num;
params->fec_encoder_schemes_num = settings->fec_encoder_schemes_num;
for (xqc_int_t i = 0; i < settings->fec_encoder_schemes_num; i++) {
params->fec_encoder_schemes[i] = settings->fec_encoder_schemes[i];
}
}
if (conn->conn_settings.enable_decode_fec) {
params->enable_decode_fec = settings->enable_decode_fec;
params->fec_decoder_schemes_num = settings->fec_decoder_schemes_num;
for (xqc_int_t i = 0; i < settings->fec_decoder_schemes_num; i++) {
params->fec_decoder_schemes[i] = settings->fec_decoder_schemes[i];
}
}
#endif
/* set other transport parameters */
if (conn->conn_type == XQC_CONN_TYPE_SERVER
&& conn->original_dcid.cid_len > 0)
{
xqc_cid_set(¶ms->original_dest_connection_id,
conn->original_dcid.cid_buf, conn->original_dcid.cid_len);
params->original_dest_connection_id_present = 1;
xqc_gen_reset_token(&conn->original_dcid,
params->stateless_reset_token,
XQC_STATELESS_RESET_TOKENLEN,
conn->engine->config->reset_token_key,
conn->engine->config->reset_token_keylen);
params->stateless_reset_token_present = 1;
xqc_log(conn->log, XQC_LOG_INFO, "|generate sr_token[%s] for cid[%s]",
xqc_sr_token_str(conn->engine, params->stateless_reset_token),
xqc_scid_str(conn->engine, &conn->original_dcid));
} else {
params->original_dest_connection_id_present = 0;
}
xqc_cid_set(¶ms->initial_source_connection_id,
conn->initial_scid.cid_buf, conn->initial_scid.cid_len);
params->initial_source_connection_id_present = 1;
params->retry_source_connection_id.cid_len = 0;
params->retry_source_connection_id_present = 0;
params->conn_option_num = settings->conn_option_num;
xqc_memcpy(params->conn_options, settings->conn_options,
sizeof(uint32_t) * settings->conn_option_num);
params->extended_ack_features = settings->extended_ack_features;
params->max_receive_timestamps_per_ack = settings->max_receive_timestamps_per_ack;
params->receive_timestamps_exponent = settings->receive_timestamps_exponent;
return XQC_OK;
}
static inline xqc_int_t
xqc_conn_check_transport_params(xqc_connection_t *conn, const xqc_transport_params_t *params)
{
/* parameters MUST NOT be larger than 2^60 */
if (params->initial_max_streams_bidi > XQC_MAX_STREAMS
|| params->initial_max_streams_uni > XQC_MAX_STREAMS
|| params->initial_max_stream_data_bidi_local > XQC_MAX_STREAMS
|| params->initial_max_stream_data_bidi_remote > XQC_MAX_STREAMS
|| params->initial_max_stream_data_uni > XQC_MAX_STREAMS)
{
return -XQC_TLS_TRANSPORT_PARAM;
}
if (conn->conn_type == XQC_CONN_TYPE_SERVER) {
/* server MUST NOT received server-only parameters from client */
if (params->original_dest_connection_id_present
|| params->preferred_address_present
|| params->retry_source_connection_id_present
|| params->stateless_reset_token_present)
{
return -XQC_TLS_TRANSPORT_PARAM;
}
}
if (conn->conn_type == XQC_CONN_TYPE_CLIENT) {
/* check retry_source_connection_id parameter if recv retry packet */
if (conn->conn_flag & XQC_CONN_FLAG_RETRY_RECVD) {
if (!params->retry_source_connection_id_present) {
return -XQC_TLS_TRANSPORT_PARAM;
}
} else if (params->retry_source_connection_id_present) {
return -XQC_TLS_TRANSPORT_PARAM;
}
}
return XQC_OK;
}
void
xqc_update_neg_info(xqc_connection_t *conn, xqc_transport_params_t params)
{
xqc_trans_settings_t *ls = &conn->local_settings;
if (!ls->enable_encode_fec) {
conn->fec_neg_fail_reason |= XQC_LOCAL_NOT_SUPPORT_ENC;
}
if (!ls->enable_decode_fec) {
conn->fec_neg_fail_reason |= XQC_LOCAL_NOT_SUPPORT_DEC;
}
if (!params.enable_encode_fec) {
conn->fec_neg_fail_reason |= XQC_REMOTE_NOT_SUPPORT_ENC;
}
if (!params.enable_decode_fec) {
conn->fec_neg_fail_reason |= XQC_REMOTE_NOT_SUPPORT_DEC;
}
}
void
xqc_check_fec_trans_param(xqc_connection_t *conn, xqc_transport_params_t params)
{
if (params.enable_encode_fec) {
if (params.fec_max_symbols_num > XQC_FEC_MAX_SYMBOL_NUM_PBLOCK) {
conn->conn_settings.enable_decode_fec = 0;
conn->local_settings.enable_decode_fec = 0;
conn->fec_neg_fail_reason |= XQC_REMOTE_PARAM_ERR;
xqc_log(conn->log, XQC_LOG_ERROR, "|quic_fec|remote_setting's max_symbol_number is too large to be decoded");
}
if (params.fec_max_symbols_num == 0) {
conn->conn_settings.enable_decode_fec = 0;
conn->local_settings.enable_decode_fec = 0;
conn->fec_neg_fail_reason |= XQC_REMOTE_PARAM_ERR;
xqc_log(conn->log, XQC_LOG_ERROR, "|quic_fec|remote_setting's max_symbol_number is zero");
}
}
}
void
xqc_conn_tls_transport_params_cb(const uint8_t *tp, size_t len, void *user_data)
{
xqc_int_t ret, re_encode_local_tp_flag = 0;
xqc_cid_t *cid;
xqc_list_head_t *node, *pos, *next;
xqc_cid_inner_t *cid_node;
xqc_transport_params_t params;
xqc_stream_t *stream;
xqc_connection_t *conn = (xqc_connection_t *)user_data;
xqc_transport_params_type_t tp_type = (conn->conn_type == XQC_CONN_TYPE_CLIENT
? XQC_TP_TYPE_ENCRYPTED_EXTENSIONS : XQC_TP_TYPE_CLIENT_HELLO);
memset(¶ms, 0, sizeof(xqc_transport_params_t));
/* decode peer's transport parameter */
ret = xqc_decode_transport_params(¶ms, tp_type, tp, len);
if (ret != XQC_OK) {
XQC_CONN_ERR(conn, TRA_TRANSPORT_PARAMETER_ERROR);
return;
}
/* validate peer's transport parameter */
ret = xqc_conn_check_transport_params(conn, ¶ms);
if (ret != XQC_OK) {
XQC_CONN_ERR(conn, TRA_TRANSPORT_PARAMETER_ERROR);
return;
}
/* check datagram parameter */
if (params.max_datagram_frame_size < conn->remote_settings.max_datagram_frame_size) {
/* 0RTT: remote_settings.max_datagram_frame_size = X */
/* 1RTT: remote_settings.max_datagram_frame_size = 0 */
XQC_CONN_ERR(conn, TRA_0RTT_TRANS_PARAMS_ERROR);
return;
}
/* set remote transport param */
ret = xqc_conn_set_remote_transport_params(conn, ¶ms, tp_type);
if (ret != XQC_OK) {
xqc_log(conn->log, XQC_LOG_ERROR,
"|xqc_conn_set_remote_transport_params failed|ret:%d|", ret);
XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR);
return;
}
xqc_log(conn->log, XQC_LOG_DEBUG, "|1RTT_transport_params|max_datagram_frame_size:%ud|",
conn->remote_settings.max_datagram_frame_size);
if ((conn->local_settings.extended_ack_features & XQC_ACK_EXT_FEATURE_BIT_RECV_TS)
&& (conn->remote_settings.extended_ack_features & XQC_ACK_EXT_FEATURE_BIT_RECV_TS)
&& conn->remote_settings.max_receive_timestamps_per_ack > 0)
{
conn->conn_settings.extended_ack_features = 2;
conn->conn_settings.max_receive_timestamps_per_ack =
conn->remote_settings.max_receive_timestamps_per_ack;
conn->conn_settings.receive_timestamps_exponent =
conn->remote_settings.receive_timestamps_exponent;
}
/* save no crypto flag */
if (params.no_crypto == 1) {
conn->remote_settings.no_crypto = 1;
conn->local_settings.no_crypto = 1;
xqc_tls_set_no_crypto(conn->tls);
}
/* sr token will only present in server's transport parameter, it means
client have already confirmed server's cid, associate the sr token with
server's cid */
if (params.stateless_reset_token_present) {
/* it is supposed to be only one existing cid in the dcid set, find the
first node and copy the sr token to that cid */
xqc_cid_set_inner_t *inner_set = xqc_get_path_cid_set(&conn->dcid_set, XQC_INITIAL_PATH_ID);
node = inner_set->cid_list.next;
if (NULL != node) {
cid_node = xqc_list_entry(node, xqc_cid_inner_t, list);
xqc_memcpy(cid_node->cid.sr_token, params.stateless_reset_token,
XQC_STATELESS_RESET_TOKENLEN);
xqc_log(conn->log, XQC_LOG_INFO, "|store sr_token with cid: %s"
"|token:%s", xqc_dcid_str(conn->engine, &cid_node->cid),
xqc_sr_token_str(conn->engine, params.stateless_reset_token));
if (xqc_find_conns_hash(conn->engine->conns_hash_sr_token, conn,
cid_node->cid.sr_token,
XQC_STATELESS_RESET_TOKENLEN) == NULL)
{
if (xqc_insert_conns_hash(conn->engine->conns_hash_sr_token, conn,
cid_node->cid.sr_token,
XQC_STATELESS_RESET_TOKENLEN))
{
xqc_log(conn->log, XQC_LOG_ERROR, "|insert sr conn hash error");
}
} else {
xqc_log(conn->log, XQC_LOG_ERROR, "|sr_token conflict:%s", xqc_sr_token_str(conn->engine, cid_node->cid.sr_token));
}
} else {
/* it's weired if sr token present while cid not confirmed */
xqc_log(conn->log, XQC_LOG_ERROR,
"|cid not confirmed while sr token present");
}
}
if (conn->conn_type == XQC_CONN_TYPE_SERVER
&& params.multipath_version != conn->local_settings.multipath_version
&& xqc_conn_is_current_mp_version_supported(params.multipath_version) == XQC_OK)
{
conn->local_settings.multipath_version = params.multipath_version;
re_encode_local_tp_flag = 1;
}
ret = xqc_conn_try_to_enable_multipath(conn);
if(ret != XQC_OK) {
xqc_log(conn->log, XQC_LOG_ERROR,
"|enable multipath error|ret:%d|", ret);
XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR);
return;
}
/* must be called after xqc_conn_try_to_enable_multipath */
xqc_conn_try_to_enable_pmtud(conn);
/* update flow control */
xqc_conn_update_flow_ctl_settings(conn);
xqc_list_for_each_safe(pos, next, &conn->conn_all_streams) {
stream = xqc_list_entry(pos, xqc_stream_t, all_stream_list);
xqc_stream_update_flow_ctl(stream);
}
/** Negotiate on whether send datagram redundancy on 1RTT packet;
* on XQC_RED_NOT_USE(default):
* No need to negotiate, and whether to send redundancy is
* completely decided by server's config;
* on XQC_RED_SET_CLOSE:
* The client's signal to close datagram redundancy, thus stop
* sending dgram redundancy.
*
*/
if (conn->conn_type == XQC_CONN_TYPE_SERVER
&& params.close_dgram_redundancy != XQC_RED_NOT_USE)
{
if (params.close_dgram_redundancy == XQC_RED_SET_CLOSE) {
conn->local_settings.close_dgram_redundancy = XQC_RED_SET_CLOSE;
conn->conn_settings.datagram_redundancy = 0;
}
re_encode_local_tp_flag = 1;
}
if (conn->local_settings.close_dgram_redundancy == XQC_RED_SET_CLOSE) {
conn->conn_settings.mp_enable_reinjection = 0;
xqc_log(conn->log, XQC_LOG_DEBUG, "|stop sending datagram redundancy.");
}
#ifdef XQC_ENABLE_FEC
xqc_update_neg_info(conn, params);
if (conn->conn_settings.enable_encode_fec
|| conn->conn_settings.enable_decode_fec)
{
// update fec settings according to remote params
xqc_check_fec_trans_param(conn, params);
ret = xqc_negotiate_fec_schemes(conn, params);
if (ret == XQC_OK) {
xqc_on_fec_negotiate_success(conn, params);
}
if (conn->conn_type == XQC_CONN_TYPE_SERVER) {
re_encode_local_tp_flag = 1;
}
}
#endif
// if local_settings needs to be updated, reencode local transport parameters
if (re_encode_local_tp_flag) {
uint8_t tp_buf[XQC_MAX_TRANSPORT_PARAM_BUF_LEN] = {0};
size_t tp_len = 0;
ret = xqc_conn_encode_local_tp(conn, tp_buf,
XQC_MAX_TRANSPORT_PARAM_BUF_LEN, &tp_len);
if (ret != XQC_OK) {
xqc_log(conn->log, XQC_LOG_ERROR,
"|cannot reset local transport parameters while multipath version is different");
XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR);
return;
}
ret = xqc_tls_update_tp(conn->tls, tp_buf, tp_len);
if (ret != XQC_OK) {
xqc_log(conn->log, XQC_LOG_ERROR, "|server tls update transport param error|ret:%d|", ret);
XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR);
return;
}
}
/* notify application layer to save transport parameter */
if (conn->transport_cbs.save_tp_cb) {
char tp_buf[8192] = {0};
ssize_t written = xqc_write_transport_params(tp_buf, sizeof(tp_buf), ¶ms);
if (written < 0) {
xqc_log(conn->log, XQC_LOG_ERROR, "|write transport params error|");
return;
}
conn->transport_cbs.save_tp_cb(tp_buf, written, conn->user_data);
}
}
static inline xqc_hs_buffer_t *
xqc_create_hs_buffer(int buf_size)
{
xqc_hs_buffer_t *buf = xqc_malloc(sizeof(xqc_hs_buffer_t) + buf_size);
if (buf == NULL) {
return NULL;
}
xqc_init_list_head(&buf->list_head);
buf->data_len = buf_size;
return buf;
}
xqc_int_t
xqc_conn_tls_crypto_data_cb(xqc_encrypt_level_t level, const uint8_t *data,
size_t len, void *user_data)
{
xqc_connection_t *conn = (xqc_connection_t *)user_data;
xqc_list_head_t *crypto_data_list = NULL;
xqc_list_head_t *retry_buff = NULL;
switch (level) {
case XQC_ENC_LEV_INIT:
/* ClientHello, ServerHello, HelloRetryRequest are from initial encryption level */
crypto_data_list = &conn->initial_crypto_data_list;
break;
case XQC_ENC_LEV_HSK:
/* Encrypted Extension, Certificate, Certificate Verify, Finished */
crypto_data_list = &conn->hsk_crypto_data_list;
break;
case XQC_ENC_LEV_1RTT:
/* New Session Ticket is from application level */
crypto_data_list = &conn->application_crypto_data_list;
break;
default:
xqc_log(conn->log, XQC_LOG_ERROR,
"|impossible crypto data from encryption level|level:%d|", level);
XQC_CONN_ERR(conn, TRA_CRYPTO_ERROR_BASE);
return -XQC_EFATAL;
}
xqc_hs_buffer_t *hs_buf = xqc_create_hs_buffer(len);
if (XQC_UNLIKELY(!hs_buf)) {
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_create_hs_buffer failed|");
return -XQC_EMALLOC;
}
/*
* should limit the length of crypto data when the TLS layer generates unlimited data
* or when the client sends duplicate ClientHello, etc.
*/
conn->crypto_data_total_len += len;
if (conn->crypto_data_total_len > XQC_CONN_MAX_CRYPTO_DATA_TOTAL_LEN) {
xqc_log(conn->log, XQC_LOG_ERROR,
"|crypto_data_total_len exceed limit|total_len:%ui|", conn->crypto_data_total_len);
xqc_free(hs_buf);
XQC_CONN_ERR(conn, TRA_CRYPTO_BUFFER_EXCEEDED);
return -XQC_EFATAL;
}
memcpy(hs_buf->data, data, len);
xqc_list_add_tail(&hs_buf->list_head, crypto_data_list);
return XQC_OK;
}
static void
xqc_settings_copy_from_transport_params(xqc_trans_settings_t *dest,
const xqc_transport_params_t *src)
{
dest->max_stream_data_bidi_local = src->initial_max_stream_data_bidi_local;
dest->max_stream_data_bidi_remote = src->initial_max_stream_data_bidi_remote;
dest->max_stream_data_uni = src->initial_max_stream_data_uni;
dest->max_data = src->initial_max_data;
dest->max_streams_bidi = src->initial_max_streams_bidi;
dest->max_streams_uni = src->initial_max_streams_uni;
dest->max_idle_timeout = src->max_idle_timeout;
dest->max_udp_payload_size = src->max_udp_payload_size;
dest->stateless_reset_token_present = src->stateless_reset_token_present;
if (src->stateless_reset_token_present) {
xqc_memcpy(dest->stateless_reset_token, src->stateless_reset_token,
sizeof(dest->stateless_reset_token));
} else {
xqc_memset(dest->stateless_reset_token, 0, sizeof(dest->stateless_reset_token));
}
dest->ack_delay_exponent = src->ack_delay_exponent;
dest->disable_active_migration = src->disable_active_migration;
dest->max_ack_delay = src->max_ack_delay;
dest->preferred_address = src->preferred_address;
dest->active_connection_id_limit = src->active_connection_id_limit;
dest->enable_multipath = src->enable_multipath;
dest->init_max_path_id = src->init_max_path_id;
dest->max_datagram_frame_size = src->max_datagram_frame_size;
dest->enable_pmtud = src->enable_pmtud;
}
xqc_int_t
xqc_conn_set_early_remote_transport_params(xqc_connection_t *conn,
const xqc_transport_params_t *params)
{
if (conn->conn_type == XQC_CONN_TYPE_SERVER) {
return -XQC_TLS_INVALID_STATE;
}
xqc_settings_copy_from_transport_params(&conn->remote_settings, params);
xqc_conn_update_flow_ctl_settings(conn);
return XQC_OK;
}
xqc_int_t
xqc_conn_tls_alpn_select_cb(const char *alpn, size_t alpn_len, void *user_data)
{
xqc_connection_t *conn = (xqc_connection_t *)user_data;
xqc_conn_server_on_alpn(conn, alpn, alpn_len);
return XQC_OK;
}
xqc_int_t
xqc_conn_tls_cert_verify_cb(const unsigned char *certs[], const size_t cert_len[],
size_t certs_len, void *user_data)
{
xqc_connection_t *conn = (xqc_connection_t *)user_data;
return conn->transport_cbs.cert_verify_cb(certs, cert_len, certs_len, conn->user_data);
}
void
xqc_conn_tls_session_cb(const char *data, size_t data_len, void *user_data)
{
xqc_connection_t *conn = (xqc_connection_t *)user_data;
conn->transport_cbs.save_session_cb(data, data_len, conn->user_data);
}
void
xqc_conn_tls_keylog_cb(const char *line, void *user_data)
{
#ifdef XQC_PRINT_SECRET
xqc_connection_t *conn = (xqc_connection_t *)user_data;
xqc_engine_t *eng = conn->engine;
/* invoke engine's callback */
if (eng->eng_callback.keylog_cb) {
eng->eng_callback.keylog_cb(&(conn->scid_set.user_scid), line, eng->user_data);
}
#endif
}
void
xqc_conn_tls_error_cb(xqc_int_t tls_err, void *user_data)
{
xqc_connection_t *conn = (xqc_connection_t *)user_data;
xqc_log(conn->log, XQC_LOG_ERROR, "|tls error|0x%xi|", tls_err);
XQC_CONN_ERR(conn, (tls_err | TRA_CRYPTO_ERROR_BASE));
}
void
xqc_free_crypto_buffer_list(xqc_list_head_t *buffer_list)
{
xqc_list_head_t *head = buffer_list;
xqc_list_head_t *pos, *next;
xqc_list_for_each_safe(pos, next, head) {
xqc_list_del(pos);
xqc_free(pos);
}
}
xqc_int_t
xqc_conn_tls_cert_cb(const char *sni, void **chain, void **crt,
void **key, void *user_data)
{
xqc_connection_t *conn = (xqc_connection_t *)user_data;
if (conn->transport_cbs.conn_cert_cb) {
return conn->transport_cbs.conn_cert_cb(
sni, chain, crt, key, conn->user_data);
}
/* if no callback is set, XQC_OK is also returned and the default cert will
be used */
return XQC_OK;
}
void
xqc_conn_tls_handshake_completed_cb(void *user_data)
{
xqc_connection_t *conn = (xqc_connection_t *)user_data;
conn->conn_flag |= XQC_CONN_FLAG_TLS_HSK_COMPLETED;
conn->handshake_complete_time = xqc_monotonic_timestamp();
}
const xqc_tls_callbacks_t xqc_conn_tls_cbs = {
.crypto_data_cb = xqc_conn_tls_crypto_data_cb,
.tp_cb = xqc_conn_tls_transport_params_cb,
.alpn_select_cb = xqc_conn_tls_alpn_select_cb,
.cert_verify_cb = xqc_conn_tls_cert_verify_cb,
.session_cb = xqc_conn_tls_session_cb,
.keylog_cb = xqc_conn_tls_keylog_cb,
.error_cb = xqc_conn_tls_error_cb,
.hsk_completed_cb = xqc_conn_tls_handshake_completed_cb,
.cert_cb = xqc_conn_tls_cert_cb,
};
xqc_msec_t
xqc_conn_get_idle_timeout(xqc_connection_t *conn)
{
if (conn->conn_type == XQC_CONN_TYPE_SERVER
&& !(conn->conn_flag & XQC_CONN_FLAG_HANDSHAKE_COMPLETED))
{
/* only server will limit idle timeout to init_idle_time_out before handshake completed */
return conn->conn_settings.init_idle_time_out == 0
? XQC_CONN_INITIAL_IDLE_TIMEOUT : conn->conn_settings.init_idle_time_out;
} else {
return conn->local_settings.max_idle_timeout == 0
? XQC_CONN_DEFAULT_IDLE_TIMEOUT : conn->local_settings.max_idle_timeout;
}
}
xqc_msec_t
xqc_conn_get_queue_fin_timeout(xqc_connection_t *conn)
{
return conn->conn_settings.fec_conn_queue_rpr_timeout;
}
void
xqc_conn_decrease_unacked_stream_ref(xqc_connection_t *conn, xqc_packet_out_t *packet_out)
{
int first_time_ack = 1;
if (packet_out->po_flag & XQC_POF_STREAM_UNACK) {
first_time_ack = first_time_ack && (!packet_out->po_acked);
if (packet_out->po_origin) {
first_time_ack = first_time_ack && (!packet_out->po_origin->po_acked);
}
if (first_time_ack) {
xqc_stream_t *stream;
for (int i = 0; i < XQC_MAX_STREAM_FRAME_IN_PO; i++) {
if (packet_out->po_stream_frames[i].ps_is_used == 0) {
break;
}
stream = xqc_find_stream_by_id(packet_out->po_stream_frames[i].ps_stream_id, conn->streams_hash);
if (stream != NULL) {
if (stream->stream_unacked_pkt == 0) {
xqc_log(conn->log, XQC_LOG_ERROR, "|stream_unacked_pkt too small|");
} else {
stream->stream_unacked_pkt--;
}
if (packet_out->po_stream_frames[i].ps_has_fin && stream->stream_stats.first_fin_ack_time == 0) {
stream->stream_stats.first_fin_ack_time = xqc_monotonic_timestamp();
}
/* Update stream state */
if (stream->stream_unacked_pkt == 0 && stream->stream_state_send == XQC_SEND_STREAM_ST_DATA_SENT) {
xqc_stream_send_state_update(stream, XQC_SEND_STREAM_ST_DATA_RECVD);
xqc_log(conn->log, XQC_LOG_DEBUG, "|stream enter DATA RECVD|stream_id:%d", stream->stream_id);
xqc_stream_maybe_need_close(stream);
}
}
}
}
packet_out->po_flag &= ~XQC_POF_STREAM_UNACK;
}
}
void
xqc_conn_increase_unacked_stream_ref(xqc_connection_t *conn, xqc_packet_out_t *packet_out)
{
if ((packet_out->po_frame_types & XQC_FRAME_BIT_STREAM)
&& !(packet_out->po_flag & XQC_POF_STREAM_UNACK))
{
if ((!packet_out->po_origin)) {
xqc_stream_t *stream;
for (int i = 0; i < XQC_MAX_STREAM_FRAME_IN_PO; i++) {
if (packet_out->po_stream_frames[i].ps_is_used == 0) {
break;
}
stream = xqc_find_stream_by_id(packet_out->po_stream_frames[i].ps_stream_id, conn->streams_hash);
if (stream != NULL) {
stream->stream_unacked_pkt++;
/* Update stream state */
if (stream->stream_state_send == XQC_SEND_STREAM_ST_READY) {
xqc_stream_send_state_update(stream, XQC_SEND_STREAM_ST_SEND);
}
if (packet_out->po_stream_frames[i].ps_has_fin
&& stream->stream_state_send == XQC_SEND_STREAM_ST_SEND)
{
xqc_stream_send_state_update(stream, XQC_SEND_STREAM_ST_DATA_SENT);
}
}
}
}
packet_out->po_flag |= XQC_POF_STREAM_UNACK;
}
}
void
xqc_conn_update_stream_stats_on_sent(xqc_connection_t *conn, xqc_send_ctl_t *ctl,
xqc_packet_out_t *packet_out, xqc_usec_t now)
{
xqc_stream_id_t stream_id;
xqc_stream_t *stream[XQC_MAX_STREAM_FRAME_IN_PO] = {0};
int stream_cnt = 0;
int i, j;
if (packet_out->po_frame_types & (XQC_FRAME_BIT_STREAM | XQC_FRAME_BIT_RESET_STREAM)) {
for (i = 0; i < XQC_MAX_STREAM_FRAME_IN_PO; i++) {
if (packet_out->po_stream_frames[i].ps_is_used == 0) {
break;
}
stream_id = packet_out->po_stream_frames[i].ps_stream_id;
stream[stream_cnt] = xqc_find_stream_by_id(stream_id, conn->streams_hash);
for (j = 0; j < stream_cnt; j++) {
if (stream[j] == stream[stream_cnt]) {
break;
}
}
if (stream[stream_cnt]) {
if (stream[stream_cnt]->stream_stats.first_snd_time == 0) {
stream[stream_cnt]->stream_stats.first_snd_time = now;
}
if (packet_out->po_stream_frames[i].ps_has_fin) {
stream[stream_cnt]->stream_stats.local_fin_snd_time = now;
if (stream[stream_cnt]->stream_stats.local_fst_fin_snd_time == 0) {
stream[stream_cnt]->stream_stats.local_fst_fin_snd_time = now;
}
}
if (packet_out->po_stream_frames[i].ps_is_reset) {
stream[stream_cnt]->stream_stats.local_reset_time = now;
}
// do not repeatedly count
if (j == stream_cnt) {
if (packet_out->po_sched_cwnd_blk_ts) {
stream[stream_cnt]->stream_stats.sched_cwnd_blk_duration += now - packet_out->po_sched_cwnd_blk_ts;
stream[stream_cnt]->stream_stats.sched_cwnd_blk_cnt++;
}
if (packet_out->po_send_cwnd_blk_ts) {
stream[stream_cnt]->stream_stats.send_cwnd_blk_duration += now - packet_out->po_send_cwnd_blk_ts;
stream[stream_cnt]->stream_stats.send_cwnd_blk_cnt++;
}
if (packet_out->po_send_pacing_blk_ts) {
stream[stream_cnt]->stream_stats.send_pacing_blk_duration += now - packet_out->po_send_pacing_blk_ts;
stream[stream_cnt]->stream_stats.send_pacing_blk_cnt++;
}
if (packet_out->po_flag & (XQC_POF_TLP | XQC_POF_LOST)) {
stream[stream_cnt]->stream_stats.retrans_pkt_cnt++;
}
stream[stream_cnt]->stream_stats.sent_pkt_cnt++;
stream[stream_cnt]->stream_stats.max_pto_backoff = xqc_max(stream[stream_cnt]->stream_stats.max_pto_backoff, ctl->ctl_pto_count);
stream_cnt++;
}
}
}
}
}
xqc_usec_t
xqc_conn_get_max_pto(xqc_connection_t *conn)
{
xqc_path_ctx_t *path = NULL;
xqc_usec_t max_pto = 0;
xqc_list_head_t *pos, *next;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
if (path->path_state != XQC_PATH_STATE_ACTIVE) {
continue;
}
max_pto = xqc_max(xqc_send_ctl_calc_pto(path->path_send_ctl), max_pto);
}
return max_pto;
}
uint32_t
xqc_conn_get_max_pto_backoff(xqc_connection_t *conn, uint8_t available_only)
{
xqc_path_ctx_t *path = NULL;
uint32_t max_pto = 0;
xqc_list_head_t *pos, *next;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
if (path->path_state != XQC_PATH_STATE_ACTIVE) {
continue;
}
if (available_only
&& path->app_path_status != XQC_APP_PATH_STATUS_AVAILABLE)
{
continue;
}
max_pto = xqc_max(path->path_send_ctl->ctl_pto_count, max_pto);
}
return max_pto;
}
xqc_usec_t
xqc_conn_get_min_srtt(xqc_connection_t *conn, xqc_bool_t available_only)
{
xqc_path_ctx_t *path = NULL;
xqc_usec_t min_srtt = XQC_MAX_UINT64_VALUE;
xqc_list_head_t *pos, *next;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
if (path->path_state != XQC_PATH_STATE_ACTIVE) {
continue;
}
if (available_only && path->app_path_status != XQC_APP_PATH_STATUS_AVAILABLE) {
continue;
}
min_srtt = xqc_min(path->path_send_ctl->ctl_srtt, min_srtt);
}
return min_srtt;
}
xqc_usec_t
xqc_conn_get_max_srtt(xqc_connection_t *conn)
{
xqc_path_ctx_t *path = NULL;
xqc_usec_t max_rtt = 0;
xqc_list_head_t *pos, *next;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
if (path->path_state != XQC_PATH_STATE_ACTIVE) {
continue;
}
max_rtt = xqc_max(path->path_send_ctl->ctl_srtt, max_rtt);
}
return max_rtt;
}
void
xqc_conn_timer_expire(xqc_connection_t *conn, xqc_usec_t now)
{
xqc_timer_expire(&conn->conn_timer_manager, now);
xqc_path_ctx_t *path = NULL;
xqc_list_head_t *pos, *next;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
if (path->path_state < XQC_PATH_STATE_CLOSED) {
xqc_timer_expire(&path->path_send_ctl->path_timer_manager, now);
}
}
}
void xqc_conn_check_app_limit(xqc_connection_t *conn)
{
xqc_path_ctx_t *path = NULL;
xqc_list_head_t *pos, *next;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
if (path->path_state != XQC_PATH_STATE_ACTIVE) {
continue;
}
if (xqc_sample_check_app_limited(&path->path_send_ctl->sampler,
path->path_send_ctl, conn->conn_send_queue))
{
xqc_pacing_on_app_limit(&path->path_send_ctl->ctl_pacing);
}
}
}
void
xqc_conn_closing(xqc_connection_t *conn)
{
/* set closing notify flag, and do notify with CANNOT_DESTROY protection
later during xqc_engine_process_conn */
conn->conn_flag |= XQC_CONN_FLAG_CLOSING_NOTIFY;
}
void
xqc_conn_closing_notify(xqc_connection_t *conn)
{
if (conn->transport_cbs.conn_closing
&& (conn->conn_flag & XQC_CONN_FLAG_CLOSING_NOTIFY))
{
conn->conn_flag &= ~XQC_CONN_FLAG_CLOSING_NOTIFY;
if (!(conn->conn_flag & XQC_CONN_FLAG_CLOSING_NOTIFIED)) {
conn->conn_flag |= XQC_CONN_FLAG_CLOSING_NOTIFIED;
conn->transport_cbs.conn_closing(conn, &conn->scid_set.user_scid, conn->conn_err, conn->user_data);
}
}
}
xqc_int_t
xqc_conn_send_path_challenge(xqc_connection_t *conn, xqc_path_ctx_t *path)
{
xqc_int_t ret = XQC_OK;
xqc_packet_out_t *packet_out;
xqc_usec_t now;
ssize_t sent;
/* send data */
if (NULL == conn->transport_cbs.write_socket_ex) {
xqc_log(conn->log, XQC_LOG_WARN, "|write_socket_ex not registered while sending PATH_CHALLENGE");
return XQC_ERROR;
}
/* generate random data for path challenge, store it to validate path_response */
ret = xqc_generate_path_challenge_data(conn, path);
if (ret != XQC_OK) {
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_generate_path_challenge_data error|%d|", ret);
return ret;
}
/* write path challenge frame & send immediately */
packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER);
if (packet_out == NULL) {
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|");
return -XQC_EWRITE_PKT;
}
ret = xqc_gen_path_challenge_frame(packet_out, path->path_challenge_data);
if (ret < 0) {
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_path_challenge_frame error|%d|", ret);
goto end;
}
packet_out->po_used_size += ret;
packet_out->po_path_flag |= XQC_PATH_SPECIFIED_BY_PCPR;
packet_out->po_path_id = path->path_id;
ret = xqc_enc_packet_with_pn(conn, path, packet_out);
if (ret != XQC_OK) {
xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_enc_packet_with_pn error|%d|", ret);
goto end;
}
/* record the send time of packet */
now = xqc_monotonic_timestamp();
packet_out->po_sent_time = now;
sent = conn->transport_cbs.write_socket_ex(path->path_id, conn->enc_pkt, conn->enc_pkt_len,
(struct sockaddr *)path->rebinding_addr,
path->rebinding_addrlen,
xqc_conn_get_user_data(conn));
if (sent != conn->enc_pkt_len) {
xqc_log(conn->log, XQC_LOG_ERROR,
"|write_socket error|conn:%p|pkt_num:%ui|size:%ud|sent:%z|pkt_type:%s|frame:%s|now:%ui|",
conn, packet_out->po_pkt.pkt_num, packet_out->po_used_size, sent,
xqc_pkt_type_2_str(packet_out->po_pkt.pkt_type),
xqc_frame_type_2_str(conn->engine, packet_out->po_frame_types), now);
ret = -XQC_ESOCKET;
goto end;
} else {
xqc_log(conn->log, XQC_LOG_INFO,
"|<==|conn:%p|pkt_num:%ui|size:%ud|sent:%z|pkt_type:%s|frame:%s|inflight:%ud|now:%ui|",
conn, packet_out->po_pkt.pkt_num, packet_out->po_used_size, sent,
xqc_pkt_type_2_str(packet_out->po_pkt.pkt_type),
xqc_frame_type_2_str(conn->engine, packet_out->po_frame_types), path->path_send_ctl->ctl_bytes_in_flight, now);
}
end:
xqc_send_queue_remove_send(&packet_out->po_list);
xqc_send_queue_insert_free(packet_out, &conn->conn_send_queue->sndq_free_packets, conn->conn_send_queue);
return ret;
}
uint64_t
xqc_conn_get_unscheduled_bytes(xqc_connection_t *conn)
{
uint64_t scheduled_bytes = 0;
uint64_t unsent_bytes_in_sndq;
xqc_list_head_t *pos, *next;
xqc_path_ctx_t *path;
xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) {
path = xqc_list_entry(pos, xqc_path_ctx_t, path_list);
if (path->path_schedule_bytes >= 0) {
scheduled_bytes += path->path_schedule_bytes;
} else {
xqc_log(conn->log, XQC_LOG_ERROR, "|negative_path_scheduled_bytes!|");
}
}
unsent_bytes_in_sndq = xqc_send_queue_get_unsent_packets_num(conn->conn_send_queue);
unsent_bytes_in_sndq = unsent_bytes_in_sndq * xqc_conn_get_mss(conn);
if (unsent_bytes_in_sndq >= scheduled_bytes) {
return unsent_bytes_in_sndq - scheduled_bytes;
} else {
xqc_log(conn->log, XQC_LOG_ERROR, "|scheduled_bytes_more_than_unsent_bytes|"
"sched_bytes:%ui|unsent_bytes:%ui|",
scheduled_bytes, unsent_bytes_in_sndq);
return 0;
}
}
xqc_conn_type_t
xqc_conn_get_type(xqc_connection_t *conn)
{
return conn->conn_type;
}
void
xqc_conn_set_pkt_filter_callback(xqc_connection_t *conn,
xqc_conn_pkt_filter_callback_pt pkt_filter_cb,
void *pkt_filter_cb_user_data)
{
conn->pkt_filter_cb = pkt_filter_cb;
conn->pkt_filter_cb_user_data = pkt_filter_cb_user_data;
}
void
xqc_conn_unset_pkt_filter_callback(xqc_connection_t *conn)
{
if (conn) {
conn->pkt_filter_cb = NULL;
conn->pkt_filter_cb_user_data = NULL;
xqc_log(conn->log, XQC_LOG_INFO, "|conn unset pkt filter callback, will"
"use write_socket again");
}
}
int
xqc_conn_buff_0rtt_datagram(xqc_connection_t *conn, void *data,
size_t data_len, uint64_t dgram_id, xqc_data_qos_level_t qos_level)
{
xqc_datagram_0rtt_buffer_t *buffer = xqc_datagram_create_0rtt_buffer(data, data_len, dgram_id, qos_level);
if (buffer == NULL) {
return -XQC_EMALLOC;
}
xqc_list_add_tail(&buffer->list, &conn->dgram_0rtt_buffer_list);
return XQC_OK;
}
void
xqc_conn_destroy_0rtt_datagram_buffer_list(xqc_connection_t *conn)
{
xqc_list_head_t *pos, *next;
xqc_datagram_0rtt_buffer_t *buffer;
xqc_list_for_each_safe(pos, next, &conn->dgram_0rtt_buffer_list) {
buffer = xqc_list_entry(pos, xqc_datagram_0rtt_buffer_t, list);
xqc_list_del_init(pos);
xqc_datagram_destroy_0rtt_buffer(buffer);
}
}
xqc_ping_record_t*
xqc_conn_create_ping_record(xqc_connection_t *conn)
{
xqc_ping_record_t *pr = xqc_calloc(1, sizeof(xqc_ping_record_t));
xqc_init_list_head(&pr->list);
xqc_list_add_tail(&pr->list, &conn->ping_notification_list);
return pr;
}
void
xqc_conn_destroy_ping_record(xqc_ping_record_t *pr)
{
xqc_list_del_init(&pr->list);
xqc_free(pr);
}
void
xqc_conn_destroy_ping_notification_list(xqc_connection_t *conn)
{
xqc_list_head_t *pos, *next;
xqc_ping_record_t *pr;
xqc_list_for_each_safe(pos, next, &conn->ping_notification_list) {
pr = xqc_list_entry(pos, xqc_ping_record_t, list);
xqc_conn_destroy_ping_record(pr);
}
}
xqc_bool_t
xqc_conn_should_clear_0rtt_ticket(xqc_int_t conn_err)
{
if (conn_err == TRA_0RTT_TRANS_PARAMS_ERROR) {
return XQC_TRUE;
}
return XQC_FALSE;
}
xqc_conn_settings_t
xqc_conn_get_conn_settings_template(xqc_conn_settings_type_t settings_type)
{
xqc_conn_settings_t conn_settings = internal_default_conn_settings;
if (settings_type == XQC_CONN_SETTINGS_LOW_DELAY) {
conn_settings.ack_frequency = 1;
conn_settings.loss_detection_pkt_thresh = 2;
conn_settings.pto_backoff_factor = 1.5;
}
return conn_settings;
}
xqc_gp_timer_id_t
xqc_conn_register_gp_timer(xqc_connection_t *conn, char *timer_name, xqc_gp_timer_timeout_pt cb, void *user_data)
{
return xqc_timer_register_gp_timer(&conn->conn_timer_manager, timer_name, cb, user_data);
}
void
xqc_conn_unregister_gp_timer(xqc_connection_t *conn, xqc_gp_timer_id_t gp_timer_id)
{
xqc_timer_unregister_gp_timer(&conn->conn_timer_manager, gp_timer_id);
}
xqc_int_t
xqc_conn_gp_timer_set(xqc_connection_t *conn, xqc_gp_timer_id_t gp_timer_id,
xqc_usec_t expire_time)
{
return xqc_timer_gp_timer_set(&conn->conn_timer_manager, gp_timer_id, expire_time);
}
xqc_int_t
xqc_conn_gp_timer_unset(xqc_connection_t *conn, xqc_gp_timer_id_t gp_timer_id)
{
return xqc_timer_gp_timer_unset(&conn->conn_timer_manager, gp_timer_id);
}
xqc_int_t
xqc_conn_gp_timer_get_info(xqc_connection_t *conn, xqc_gp_timer_id_t gp_timer_id,
xqc_bool_t *is_set, xqc_usec_t *expire_time)
{
return xqc_timer_gp_timer_get_info(&conn->conn_timer_manager, gp_timer_id, is_set, expire_time);
}
/**
* @brief get public local transport settings.
*/
xqc_conn_public_local_trans_settings_t
xqc_conn_get_public_local_trans_settings(xqc_connection_t *conn)
{
xqc_conn_public_local_trans_settings_t settings;
settings.max_datagram_frame_size = conn->local_settings.max_datagram_frame_size;
return settings;
}
/**
* @brief set public local transport settings
*/
void
xqc_conn_set_public_local_trans_settings(xqc_connection_t *conn,
xqc_conn_public_local_trans_settings_t *settings)
{
if (conn == NULL || settings == NULL) {
return;
}
if (settings->max_datagram_frame_size != conn->local_settings.max_datagram_frame_size) {
conn->local_settings.max_datagram_frame_size = settings->max_datagram_frame_size;
conn->conn_settings.max_datagram_frame_size = settings->max_datagram_frame_size;
conn->conn_flag |= XQC_CONN_FLAG_LOCAL_TP_UPDATED;
}
}
/**
* @brief get public remote transport settings.
*/
xqc_conn_public_remote_trans_settings_t
xqc_conn_get_public_remote_trans_settings(xqc_connection_t *conn)
{
xqc_conn_public_remote_trans_settings_t settings;
settings.max_datagram_frame_size = conn->remote_settings.max_datagram_frame_size;
return settings;
}
/**
* @brief set public remote transport settings
*/
void
xqc_conn_set_public_remote_trans_settings(xqc_connection_t *conn,
xqc_conn_public_remote_trans_settings_t *settings)
{
conn->remote_settings.max_datagram_frame_size = settings->max_datagram_frame_size;
}
void
xqc_conn_reset(xqc_connection_t *conn)
{
xqc_conn_shutdown(conn);
/* set error code and close message, notify to application */
conn->conn_state = XQC_CONN_STATE_DRAINING;
xqc_log_event(conn->log, CON_CONNECTION_STATE_UPDATED, conn);
conn->conn_err = XQC_ESTATELESS_RESET;
XQC_CONN_CLOSE_MSG(conn, "stateless reset");
xqc_conn_closing(conn);
}
xqc_int_t
xqc_conn_handle_stateless_reset(xqc_connection_t *conn,
const uint8_t *sr_token)
{
xqc_int_t ret;
int res;
xqc_list_head_t *pos, *next;
xqc_cid_inner_t *cid;
xqc_cid_set_inner_t *inner_set;
xqc_list_head_t *pos_set, *next_set;
if (NULL == conn || NULL == sr_token) {
return -XQC_EPARAM;
}
if (conn->conn_state >= XQC_CONN_STATE_DRAINING) {
xqc_log(conn->log, XQC_LOG_INFO, "|conn closing, ignore pkt");
return XQC_OK;
}
/* compare received stateless reset token with the ones peer sent */
xqc_list_for_each_safe(pos_set, next_set, &conn->dcid_set.cid_set_list) {
inner_set = xqc_list_entry(pos_set, xqc_cid_set_inner_t, next);
xqc_list_for_each_safe(pos, next, &inner_set->cid_list) {
cid = xqc_list_entry(pos, xqc_cid_inner_t, list);
res = xqc_memcmp(sr_token, cid->cid.sr_token,
XQC_STATELESS_RESET_TOKENLEN);
if (0 == res) {
xqc_log(conn->log, XQC_LOG_INFO, "|====>|receive stateless reset"
"|cid:%s|sr_token:%s|",
xqc_dcid_str(conn->engine, &cid->cid),
xqc_sr_token_str(conn->engine, sr_token));
xqc_log_event(conn->log, TRA_STATELESS_RESET, conn);
if (inner_set->set_state < XQC_CID_SET_ABANDONED
&& cid->state < XQC_CID_RETIRED)
{
/* only responds for non-retired CIDs */
/* stateless reset received, close connection */
xqc_conn_reset(conn);
}
goto end;
}
}
}
/* sr_token not matched */
return -XQC_ERROR;
end:
return XQC_OK;
}
xqc_int_t
xqc_conn_available_paths(xqc_engine_t *engine, const xqc_cid_t *cid)
{
xqc_int_t available_paths = 0;
xqc_connection_t *conn = xqc_engine_conns_hash_find(engine, cid, 's');
if (conn == NULL) {
/* no connection found */
return available_paths;
}
xqc_path_ctx_t *path;
xqc_list_head_t *path_pos, *path_next;
xqc_list_for_each_safe(path_pos, path_next, &conn->conn_paths_list) {
path = xqc_list_entry(path_pos, xqc_path_ctx_t, path_list);
if (path->path_state < XQC_PATH_STATE_VALIDATING) {
continue;
}
if (path->path_state == XQC_PATH_STATE_ACTIVE) {
available_paths++;
}
}
xqc_log(conn->log, XQC_LOG_DEBUG, "|xqc_conn_available_paths|%" PRId32 "|", available_paths);
return available_paths;
}
#ifdef XQC_COMPAT_GENERATE_SR_PKT
xqc_int_t
xqc_conn_handle_deprecated_stateless_reset(xqc_connection_t *conn,
const xqc_cid_t *scid)
{
if (NULL == conn) {
return -XQC_EPARAM;
}
if (conn->conn_state >= XQC_CONN_STATE_DRAINING) {
xqc_log(conn->log, XQC_LOG_INFO, "|conn closing, ignore pkt");
return XQC_OK;
}
xqc_log(conn->log, XQC_LOG_INFO, "|====>|receive stateless reset"
"|deprecated|cid:%s", xqc_dcid_str(conn->engine, scid));
/* reset state of connection */
xqc_conn_reset(conn);
return XQC_OK;
}
#endif