diff --git a/demo/demo_client.c b/demo/demo_client.c index cf408ac4..48627f2d 100644 --- a/demo/demo_client.c +++ b/demo/demo_client.c @@ -40,7 +40,7 @@ #define XQC_PACKET_TMP_BUF_LEN 1600 #define MAX_BUF_SIZE (100*1024*1024) #define XQC_INTEROP_TLS_GROUPS "X25519:P-256:P-384:P-521" -#define MAX_PATH_CNT 2 +#define MAX_PATH_CNT 16 typedef enum xqc_demo_cli_alpn_type_s { @@ -183,6 +183,7 @@ typedef struct xqc_demo_cli_quic_config_s { uint8_t mp_version; uint64_t init_max_path_id; + uint64_t extra_paths_num; /* support interop test */ int is_interop_mode; @@ -190,6 +191,9 @@ typedef struct xqc_demo_cli_quic_config_s { uint8_t send_path_standby; xqc_msec_t path_status_timer_threshold; + xqc_msec_t path_cid_rotation; + xqc_msec_t path_cid_retirement; + uint64_t least_available_cid_count; uint64_t idle_timeout; @@ -419,6 +423,8 @@ typedef struct xqc_demo_cli_user_conn_s { struct event *ev_delay_req; struct event *ev_idle_restart; struct event *ev_close_path; + struct event *ev_cid_rotation; + struct event *ev_path_cid_retirement; struct event *ev_rebinding_p0; struct event *ev_rebinding_p1; @@ -430,6 +436,8 @@ typedef struct xqc_demo_cli_user_conn_s { xqc_msec_t path_status_time; xqc_msec_t path_status_timer_threshold; + int trigger_cid_rotation; + xqc_msec_t cid_rotation_timer_threshold; xqc_msec_t path_create_time; xqc_flag_t remove_path_flag; @@ -495,6 +503,11 @@ xqc_demo_cli_close_task(xqc_demo_cli_task_t *task) user_conn->ev_close_path = NULL; } + if (user_conn->ev_cid_rotation) { + event_del(user_conn->ev_cid_rotation); + user_conn->ev_cid_rotation = NULL; + } + if (user_conn->ev_rebinding_p0) { event_del(user_conn->ev_rebinding_p0); user_conn->ev_rebinding_p0 = NULL; @@ -858,16 +871,48 @@ xqc_demo_cli_conn_create_path(const xqc_cid_t *cid, void *conn_user_data) xqc_demo_cli_user_conn_t *user_conn = conn_user_data; xqc_demo_cli_ctx_t *ctx = user_conn->ctx; uint64_t path_id; - int ret; + int ret, i; int backup = 0; - printf("ready to create path notify\n"); + printf("ready to create path notify: trying to create %"PRIu64" paths, current path %i, ifcnt: %i, init_max_path_id: %"PRIu64"\n", + ctx->args->quic_cfg.extra_paths_num, + user_conn->total_path_cnt, + ctx->args->net_cfg.ifcnt, + ctx->args->quic_cfg.init_max_path_id); - if (user_conn->total_path_cnt < ctx->args->net_cfg.ifcnt - && user_conn->total_path_cnt < ctx->args->quic_cfg.init_max_path_id) - { + for (i = 0; i < ctx->args->quic_cfg.extra_paths_num; i++) { + + if (user_conn->total_path_cnt < ctx->args->net_cfg.ifcnt + && user_conn->total_path_cnt <= ctx->args->quic_cfg.init_max_path_id) + { - if (user_conn->total_path_cnt == 1 && ctx->args->quic_cfg.mp_backup) { - backup = 1; + if (user_conn->total_path_cnt == 1 && ctx->args->quic_cfg.mp_backup) { + backup = 1; + } + + ret = xqc_conn_create_path(ctx->engine, &(user_conn->cid), &path_id, backup); + if (ret < 0) { + printf("not support mp, xqc_conn_create_path err = %d path_id: %"PRIu64"\n", ret, path_id); + return; + } + + printf("client created path: path_id %"PRIu64"\n", path_id); + + ret = xqc_demo_cli_init_user_path(user_conn, user_conn->total_path_cnt, path_id); + if (ret < 0) { + printf("xqc_demo_cli_init_user_path fail: path_id %"PRIu64"\n", path_id); + xqc_conn_close_path(ctx->engine, &(user_conn->cid), path_id); + return; + } + + user_conn->path_create_time = xqc_now(); + + if (user_conn->total_path_cnt == 2 && ctx->args->quic_cfg.mp_backup) { + if (backup == 1) { + printf("Init No.%d path (id = %"PRIu64") to STANDBY state\n", 1, path_id); + } + printf("set No.%d path (id = %"PRIu64") to STANDBY state\n", 1, path_id); + xqc_conn_mark_path_standby(ctx->engine, &(user_conn->cid), path_id); + } } ret = xqc_conn_create_path(ctx->engine, &(user_conn->cid), &path_id, backup); @@ -892,7 +937,6 @@ xqc_demo_cli_conn_create_path(const xqc_cid_t *cid, void *conn_user_data) printf("set No.%d path (id = %"PRIu64") to STANDBY state\n", 1, path_id); xqc_conn_mark_path_standby(ctx->engine, &(user_conn->cid), path_id); } - } } @@ -1467,6 +1511,11 @@ xqc_demo_cli_idle_callback(int fd, short what, void *arg) user_conn->ev_close_path = NULL; } + if (user_conn->ev_cid_rotation) { + event_del(user_conn->ev_cid_rotation); + user_conn->ev_cid_rotation = NULL; + } + if (user_conn->ev_rebinding_p0) { event_del(user_conn->ev_rebinding_p0); user_conn->ev_rebinding_p0 = NULL; @@ -1539,6 +1588,39 @@ xqc_demo_cli_close_path_timeout(int fd, short what, void *arg) } } +static void +xqc_demo_cli_trigger_path_cid_rotation(int fd, short what, void *arg) +{ + xqc_demo_cli_user_conn_t *user_conn = (xqc_demo_cli_user_conn_t *) arg; + printf("xqc_demo_cli_cid_rotation: active path: %d\n", user_conn->active_path_cnt); + if (user_conn->active_path_cnt > 0) { + uint64_t path_id = 1; + printf("trigger cid rotation on path: path_id %"PRIu64"\n", path_id); + int ret = xqc_conn_trigger_cid_rotation_on_path(user_conn->ctx->engine, &(user_conn->cid), path_id); + if (ret != XQC_OK) { + printf("trigger cid rotation on path: path_id %"PRIu64" failed\n", path_id); + } else { + user_conn->trigger_cid_rotation = 1; + } + } +} + +static void +xqc_demo_cli_trigger_path_cid_retirment(int fd, short what, void *arg) +{ + xqc_demo_cli_user_conn_t *user_conn = (xqc_demo_cli_user_conn_t *) arg; + uint64_t path_id = 1; + + if (!user_conn->trigger_cid_rotation) { + printf("can't trigger cid retirement on path: path_id %"PRIu64" because the cid rotation failed\n", path_id); + return; + } + + printf("trigger cid retirement on path: path_id %"PRIu64"\n", path_id); + xqc_conn_trigger_cid_retirement_on_path(user_conn->ctx->engine, &(user_conn->cid), path_id); +} + + static void xqc_demo_cli_rebind_path0(int fd, short what, void *arg) { @@ -1727,6 +1809,7 @@ xqc_demo_cli_init_args(xqc_demo_cli_client_args_t *args) args->quic_cfg.mp_version = XQC_MULTIPATH_10; args->quic_cfg.init_max_path_id = 2; args->quic_cfg.max_pkt_sz = 1200; + args->quic_cfg.extra_paths_num = 1; args->req_cfg.throttled_req = -1; @@ -1745,7 +1828,7 @@ xqc_demo_cli_parse_server_addr(char *url, xqc_demo_cli_net_config_t *cfg) /* set hint for hostname resolve */ struct addrinfo hints = {0}; memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6: set to AF_INET if only using IPv4 */ hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ hints.ai_protocol = 0; /* Any protocol */ @@ -1860,7 +1943,7 @@ void xqc_demo_cli_parse_args(int argc, char *argv[], xqc_demo_cli_client_args_t *args) { int ch = 0; - while ((ch = getopt(argc, argv, "a:p:c:Ct:S:0m:A:D:l:L:k:K:U:u:dMoi:w:Ps:bZ:NQT:R:V:B:I:n:eEF:G:r:x:y:Y:f:")) != -1) { + while ((ch = getopt(argc, argv, "a:p:c:Ct:S:0m:A:D:l:L:k:K:U:u:dMoi:w:Ps:bz:Z:NQT:R:V:B:I:n:eEF:g:G:r:x:X:y:Y:f:")) != -1) { switch (ch) { /* server ip */ case 'a': @@ -2013,10 +2096,11 @@ xqc_demo_cli_parse_args(int argc, char *argv[], xqc_demo_cli_client_args_t *args printf("option adding interface: %s\n", optarg); if (args->net_cfg.ifcnt < MAX_PATH_CNT) { strncpy(args->net_cfg.iflist[args->net_cfg.ifcnt++], optarg, strlen(optarg)); - } else { + } + /*} else { printf("too many interfaces (two at most)!\n"); exit(0); - } + }*/ break; case 'w': @@ -2075,6 +2159,22 @@ xqc_demo_cli_parse_args(int argc, char *argv[], xqc_demo_cli_client_args_t *args args->quic_cfg.path_status_timer_threshold = atoi(optarg) * 1000; break; + case 'z': + printf("option multipath trigger cid rotation: %s ms\n", optarg); + args->quic_cfg.path_cid_rotation = atoi(optarg); + break; + + case 'g': + printf("option force a cid rotation after %s ms\n", optarg); + args->quic_cfg.path_cid_retirement = atoi(optarg); + break; + + case 'X': + printf("option create path numbers: %s\n", optarg); + args->quic_cfg.extra_paths_num = atoi(optarg); + args->quic_cfg.init_max_path_id = args->quic_cfg.extra_paths_num; + break; + case 'I': printf("option idle gap: %s\n", optarg); args->req_cfg.idle_gap = atoi(optarg); @@ -2625,6 +2725,28 @@ xqc_demo_cli_start(xqc_demo_cli_user_conn_t *user_conn, xqc_demo_cli_client_args event_add(user_conn->ev_close_path, &tv); } + if (args->quic_cfg.path_cid_rotation) { + user_conn->ev_cid_rotation = event_new(user_conn->ctx->eb, -1, 0, + xqc_demo_cli_trigger_path_cid_rotation, + user_conn); + struct timeval tv = { + .tv_sec = args->quic_cfg.path_cid_rotation / 1000, + .tv_usec = (args->quic_cfg.path_cid_rotation % 1000) * 1000, + }; + event_add(user_conn->ev_cid_rotation, &tv); + } + + if (args->quic_cfg.path_cid_retirement) { + user_conn->ev_path_cid_retirement = event_new(user_conn->ctx->eb, -1, 0, + xqc_demo_cli_trigger_path_cid_retirment, + user_conn); + struct timeval tv = { + .tv_sec = args->quic_cfg.path_cid_retirement / 1000, + .tv_usec = (args->quic_cfg.path_cid_retirement % 1000) * 1000, + }; + event_add(user_conn->ev_path_cid_retirement, &tv); + } + if (args->net_cfg.rebind_p0) { user_conn->ev_rebinding_p0 = event_new(user_conn->ctx->eb, -1, 0, xqc_demo_cli_rebind_path0, diff --git a/include/xquic/xqc_configure.h b/include/xquic/xqc_configure.h index fa13e49b..2ef3e9d4 100644 --- a/include/xquic/xqc_configure.h +++ b/include/xquic/xqc_configure.h @@ -9,3 +9,8 @@ /* #undef XQC_ENABLE_MP_INTEROP */ /* #undef XQC_NO_PID_PACKET_PROCESS */ /* #undef XQC_PROTECT_POOL_MEM */ +/* #undef XQC_COMPAT_DUPLICATE */ +/* #undef XQC_ENABLE_FEC */ +/* #undef XQC_ENABLE_XOR */ +/* #undef XQC_ENABLE_RSC */ +/* #undef XQC_ENABLE_PKM */ diff --git a/include/xquic/xqc_errno.h b/include/xquic/xqc_errno.h index 894f1c9d..e229da56 100644 --- a/include/xquic/xqc_errno.h +++ b/include/xquic/xqc_errno.h @@ -279,6 +279,14 @@ typedef enum { XQC_QPACK_ERR_MAX, } xqc_qpack_error_t; +typedef enum { + XQC_PATH_NO_ERROR = 0x0, + XQC_PATH_APPLICATION_ABANDON = 0x004150504142414E, /* Path abandon error code: APPLICATION_ABANDON */ + XQC_PATH_RESOURCE_LIMIT_REACHED = 0x0052534C494D4954, /* Path abandon error code: RESOURCE_LIMIT_REACHED */ + XQC_PATH_UNSTABLE_INTERFACE = 0x00554e5f494e5446, /* Path abandon error code: UNSTABLE_INTERFACE */ + XQC_PATH_NO_CID_AVAILABLE = 0x004e4f5f4349445f, /* Path abandon error code: NO_CID_AVAILABLE */ +} xqc_multipath_error_t; + #define QPACK_ERR_START 900 static const int QPACK_ERR_CNT = XQC_QPACK_ERR_MAX - QPACK_ERR_START; diff --git a/include/xquic/xquic.h b/include/xquic/xquic.h index 392caf49..d87ef9a1 100644 --- a/include/xquic/xquic.h +++ b/include/xquic/xquic.h @@ -1246,7 +1246,9 @@ typedef struct xqc_linger_s { typedef enum { XQC_ERR_MULTIPATH_VERSION = 0x00, - XQC_MULTIPATH_10 = 0x0a, + XQC_MULTIPATH_10 = 0x0a, + XQC_MULTIPATH_11 = 0x0b, + XQC_MULTIPATH_12 = 0x0c, } xqc_multipath_version_t; typedef enum { @@ -1456,7 +1458,7 @@ typedef enum { } xqc_0rtt_flag_t; -#define XQC_MAX_PATHS_COUNT 8 +#define XQC_MAX_PATHS_COUNT 32 #define XQC_CONN_INFO_LEN 400 typedef struct xqc_path_metrics_s { @@ -1504,6 +1506,7 @@ typedef struct xqc_conn_stats_s { * 0: 不支持MP * 1: 支持MP, 采用 Single PNS * 2: 支持MP, 采用 Multiple PNS + * */ int enable_multipath; @@ -2139,7 +2142,15 @@ XQC_EXPORT_PUBLIC_API xqc_int_t xqc_path_get_local_addr(xqc_connection_t *conn, uint64_t path_id, struct sockaddr *addr, socklen_t addr_cap, socklen_t *local_addr_len); - + +/* + * These 2 APIs below is only used for IETF interop tests. Please don't use them for formal logic! + * */ +XQC_EXPORT_PUBLIC_API +xqc_int_t xqc_conn_trigger_cid_rotation_on_path(xqc_engine_t *engine, const xqc_cid_t *scid, uint64_t path_id); + +XQC_EXPORT_PUBLIC_API +xqc_int_t xqc_conn_trigger_cid_retirement_on_path(xqc_engine_t *engine, const xqc_cid_t *scid, uint64_t path_id); /** * @brief load balance cid encryption. diff --git a/src/transport/xqc_cid.h b/src/transport/xqc_cid.h index 39dbdf98..6a389d44 100644 --- a/src/transport/xqc_cid.h +++ b/src/transport/xqc_cid.h @@ -50,6 +50,7 @@ typedef struct xqc_cid_set_inner_s { }; xqc_cid_set_state_t set_state; uint32_t acked_unused; + uint32_t cids_blocked_sent; } xqc_cid_set_inner_t; typedef struct xqc_cid_set_s { diff --git a/src/transport/xqc_conn.c b/src/transport/xqc_conn.c index 55704b29..17d3f2e5 100644 --- a/src/transport/xqc_conn.c +++ b/src/transport/xqc_conn.c @@ -729,13 +729,17 @@ xqc_conn_create(xqc_engine_t *engine, xqc_cid_t *dcid, xqc_cid_t *scid, } if (xqc_conn_is_current_mp_version_supported(xc->conn_settings.multipath_version) != XQC_OK) { - xc->conn_settings.multipath_version = XQC_MULTIPATH_10; + xc->conn_settings.multipath_version = XQC_MULTIPATH_11; } if (xc->conn_settings.init_max_path_id == 0) { xc->conn_settings.init_max_path_id = XQC_DEFAULT_INIT_MAX_PATH_ID; } + if (xc->max_paths_count == 0) { + xc->max_paths_count = XQC_MAX_PATHS_COUNT; + } + if (xc->conn_settings.probing_pkt_out_size == 0) { xc->conn_settings.probing_pkt_out_size = engine->default_conn_settings.probing_pkt_out_size; } @@ -3230,7 +3234,7 @@ xqc_conn_info_print(xqc_connection_t *conn, xqc_conn_stats_t *conn_stats) 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," + ret = snprintf(buff, buff_size, "%s,%"PRIu64",%"PRIu64",%"PRIu64",%u,%u,%u," "%u,%u,%u,%u,%u,%u,%u,%"PRIu64",%"PRIu64",%"PRIu64",i%u," #ifdef XQC_ENABLE_FEC "%u,%u,%u,%u,%u,%u,%u," @@ -3790,10 +3794,10 @@ xqc_conn_update_flow_ctl_settings(xqc_connection_t *conn) } xqc_int_t -xqc_conn_add_path_cid_sets(xqc_connection_t *conn, uint32_t start, uint32_t end) +xqc_conn_add_path_cid_sets(xqc_connection_t *conn, uint64_t start, uint64_t end) { if (conn->enable_multipath) { - uint32_t path_id; + uint64_t path_id; xqc_int_t ret; /* add cid_set_inner for all paths */ for (path_id = start; path_id <= end; path_id++) { @@ -4623,7 +4627,7 @@ xqc_conn_get_available_path_id(xqc_connection_t *conn, uint64_t *path_id) /* 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->scid_set, scid_inner_set->path_id); + 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) { diff --git a/src/transport/xqc_conn.h b/src/transport/xqc_conn.h index 73a8bef4..ad16da01 100644 --- a/src/transport/xqc_conn.h +++ b/src/transport/xqc_conn.h @@ -370,9 +370,10 @@ struct xqc_connection_s { xqc_path_ctx_t *conn_initial_path; xqc_list_head_t conn_paths_list; uint64_t validating_path_id; - uint32_t create_path_count; - uint32_t validated_path_count; - uint32_t active_path_count; + uint64_t create_path_count; + uint64_t validated_path_count; + uint64_t active_path_count; + uint64_t max_paths_count; uint64_t curr_max_path_id; uint64_t local_max_path_id; @@ -694,7 +695,7 @@ 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); xqc_int_t xqc_conn_try_to_enable_multipath(xqc_connection_t *conn); -xqc_int_t xqc_conn_add_path_cid_sets(xqc_connection_t *conn, uint32_t start, uint32_t end); +xqc_int_t xqc_conn_add_path_cid_sets(xqc_connection_t *conn, uint64_t start, uint64_t end); #endif /* _XQC_CONN_H_INCLUDED_ */ diff --git a/src/transport/xqc_engine.c b/src/transport/xqc_engine.c index ecc57e9e..fbd4d62a 100644 --- a/src/transport/xqc_engine.c +++ b/src/transport/xqc_engine.c @@ -651,7 +651,7 @@ xqc_engine_process_conn(xqc_connection_t *conn, xqc_usec_t now) xqc_log(conn->log, XQC_LOG_DEBUG, "|conn:%p|state:%s|flag:%s|now:%ui|", conn, xqc_conn_state_2_str(conn->conn_state), xqc_conn_flag_2_str(conn, conn->conn_flag), now); - int ret; + int ret = 0, rc = 0; xqc_bool_t wait_scid, wait_dcid; xqc_conn_timer_expire(conn, now); @@ -733,11 +733,34 @@ xqc_engine_process_conn(xqc_connection_t *conn, xqc_usec_t now) } if (conn->enable_multipath) { + ret = xqc_conn_get_available_path_id(conn, NULL); if ((conn->conn_flag & XQC_CONN_FLAG_MP_WAIT_MP_READY) - && xqc_conn_get_available_path_id(conn, NULL) == XQC_OK) + && ret == XQC_OK) { conn->conn_flag |= XQC_CONN_FLAG_MP_READY_NOTIFY; conn->conn_flag &= ~XQC_CONN_FLAG_MP_WAIT_MP_READY; + } else if (ret != XQC_OK && xqc_conn_check_handshake_completed(conn)) { + /* not enough cid for new path id */ + uint64_t path_id = conn->create_path_count; + xqc_cid_set_inner_t *dcid_inner_set = xqc_get_path_cid_set(&conn->dcid_set, path_id); + if (dcid_inner_set && dcid_inner_set->unused_cnt == 0 && !dcid_inner_set->cids_blocked_sent) { + rc = xqc_write_path_cids_blocked_to_packet(conn, path_id); + if (rc) { + xqc_log(conn->log, XQC_LOG_WARN, "|xqc_write_path_cids_blocked_to_packet error|ret:%ui|", ret); + } + dcid_inner_set->cids_blocked_sent = 1; + } + } + + xqc_log(conn->log, XQC_LOG_DEBUG, "|create_path_count:%ui|remote_max_path_id:%ui|", + conn->create_path_count, conn->remote_max_path_id); + if (conn->create_path_count >= conn->remote_max_path_id + 1 + && conn->conn_type == XQC_CONN_TYPE_CLIENT) + { + ret = xqc_write_path_blocked_to_packet(conn, conn->remote_max_path_id); + if (ret) { + xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_path_blocked_to_packet error|"); + } } } diff --git a/src/transport/xqc_frame.c b/src/transport/xqc_frame.c index 5ef9ebd0..47182180 100644 --- a/src/transport/xqc_frame.c +++ b/src/transport/xqc_frame.c @@ -352,7 +352,26 @@ xqc_process_frames(xqc_connection_t *conn, xqc_packet_in_t *packet_in) ret = -XQC_EMP_INVALID_MP_VERTION; } break; + case XQC_TRANS_FRAME_TYPE_PATH_BLOCKED: + if (conn->conn_settings.multipath_version >= XQC_MULTIPATH_11) { + ret = xqc_process_path_blocked_frame(conn, packet_in); + } else { + xqc_log(conn->log, XQC_LOG_ERROR, "|mp_version error|v:%ud|f:%xL|", + conn->conn_settings.multipath_version, frame_type); + ret = -XQC_EMP_INVALID_MP_VERTION; + } + break; + case XQC_TRANS_FRAME_TYPE_PATH_CIDS_BLOCKED: + if (conn->conn_settings.multipath_version >= XQC_MULTIPATH_12) { + ret = xqc_process_path_cids_blocked_frame(conn, packet_in); + + } else { + xqc_log(conn->log, XQC_LOG_ERROR, "|mp_version error|v:%ud|f:%xL|", + conn->conn_settings.multipath_version, frame_type); + ret = -XQC_EMP_INVALID_MP_VERTION; + } + break; #ifdef XQC_ENABLE_FEC case 0xfec5: if (conn->conn_settings.enable_decode_fec @@ -780,7 +799,8 @@ xqc_process_new_conn_id_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in { xqc_int_t ret = XQC_ERROR; xqc_cid_t new_conn_cid; - uint64_t retire_prior_to, curr_rpi; + uint64_t retire_prior_to = 0; + int64_t curr_rpi = 0; xqc_cid_inner_t *inner_cid; xqc_list_head_t *pos, *next; @@ -837,7 +857,7 @@ xqc_process_new_conn_id_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in return XQC_OK; } - if (retire_prior_to > curr_rpi) { + if (retire_prior_to > (uint64_t)curr_rpi) { /* * Upon receipt of an increased Retire Prior To field, the peer MUST stop using the * corresponding connection IDs and retire them with RETIRE_CONNECTION_ID frames before @@ -909,7 +929,8 @@ xqc_int_t xqc_process_retire_conn_id_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in) { xqc_int_t ret = XQC_ERROR; - uint64_t seq_num = 0, largest_scid_seq_num = 0; + uint64_t seq_num = 0; + int64_t largest_scid_seq_num = 0; ret = xqc_parse_retire_conn_id_frame(packet_in, &seq_num); if (ret != XQC_OK) { @@ -930,7 +951,7 @@ xqc_process_retire_conn_id_frame(xqc_connection_t *conn, xqc_packet_in_t *packet return -XQC_EPROTO; } - if (seq_num > largest_scid_seq_num) { + if (seq_num > (uint64_t)largest_scid_seq_num) { /* * Receipt of a RETIRE_CONNECTION_ID frame containing a sequence number * greater than any previously sent to the peer MUST be treated as a @@ -1670,7 +1691,8 @@ xqc_process_path_abandon_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_i uint64_t path_id = 0; uint64_t error_code; - ret = xqc_parse_path_abandon_frame(packet_in, &path_id, &error_code); + ret = xqc_parse_path_abandon_frame(packet_in, &path_id, &error_code, + (conn->remote_settings.multipath_version == XQC_MULTIPATH_10? 1 : 0)); if (ret != XQC_OK) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_parse_path_abandon_frame error|"); return ret; @@ -1713,7 +1735,8 @@ xqc_process_path_abandon_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_i } } - xqc_log(conn->log, XQC_LOG_DEBUG, "|path:%ui|state:%d|err_code:%ui|", path->path_id, path->path_state, error_code); + xqc_log(conn->log, XQC_LOG_DEBUG, "|path:%ui|state:%d|err_code:%ui|", path->path_id, path->path_state, + error_code); return XQC_OK; } @@ -1771,7 +1794,8 @@ xqc_process_mp_new_conn_id_frame(xqc_connection_t *conn, xqc_packet_in_t *packet { xqc_int_t ret = XQC_ERROR; xqc_cid_t new_conn_cid; - uint64_t retire_prior_to, curr_rpi; + uint64_t retire_prior_to = 0; + int64_t curr_rpi = 0; xqc_cid_inner_t *inner_cid; xqc_list_head_t *pos, *next; @@ -1793,9 +1817,9 @@ xqc_process_mp_new_conn_id_frame(xqc_connection_t *conn, xqc_packet_in_t *packet return -XQC_EILLEGAL_FRAME; } - xqc_log(conn->log, XQC_LOG_DEBUG, "|new_conn_id|%s|sr_token:%s", + xqc_log(conn->log, XQC_LOG_DEBUG, "|new_conn_id|%s|sr_token:%s|path_id:%ui|prior:%ui|", xqc_scid_str(conn->engine, &new_conn_cid), - xqc_sr_token_str(conn->engine, new_conn_cid.sr_token)); + xqc_sr_token_str(conn->engine, new_conn_cid.sr_token), path_id, retire_prior_to); if (retire_prior_to > new_conn_cid.cid_seq_num) { /* @@ -1811,6 +1835,9 @@ xqc_process_mp_new_conn_id_frame(xqc_connection_t *conn, xqc_packet_in_t *packet /* TODO: write_retire_conn_id_frame 可能涉及到 替换 path.dcid (当前无 retire_prior_to 因此不涉及) */ curr_rpi = xqc_cid_set_get_largest_seq_or_rpt(&conn->dcid_set, path_id); + xqc_log(conn->log, XQC_LOG_DEBUG, "|new_conn_id|%s|path_id:%ui|prior:%ui|", + xqc_scid_str(conn->engine, &new_conn_cid), path_id, curr_rpi); + if (curr_rpi < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|current retire_prior_to error:%i|path:%ui|", curr_rpi, path_id); @@ -1838,7 +1865,7 @@ xqc_process_mp_new_conn_id_frame(xqc_connection_t *conn, xqc_packet_in_t *packet return XQC_OK; } - if (retire_prior_to > curr_rpi) { + if (retire_prior_to > (uint64_t)curr_rpi) { /* * Upon receipt of an increased Retire Prior To field, the peer MUST stop using the * corresponding connection IDs and retire them with RETIRE_CONNECTION_ID frames before @@ -1897,11 +1924,11 @@ xqc_process_mp_new_conn_id_frame(xqc_connection_t *conn, xqc_packet_in_t *packet conn->local_settings.active_connection_id_limit, path_id); if (ret != XQC_OK) { - xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_cid_set_insert_cid error|limit:%ui|unused:%i|used:%i|path:%ui|", + xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_cid_set_insert_cid error|limit:%ui|unused:%i|used:%i|path:%ui|ret:%i|", conn->local_settings.active_connection_id_limit, xqc_cid_set_get_unused_cnt(&conn->dcid_set, path_id), xqc_cid_set_get_used_cnt(&conn->dcid_set, path_id), - path_id); + path_id, ret); return ret; } @@ -1912,7 +1939,8 @@ xqc_int_t xqc_process_mp_retire_conn_id_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in) { xqc_int_t ret = XQC_ERROR; - uint64_t seq_num = 0, largest_scid_seq_num = 0, path_id; + uint64_t seq_num = 0, path_id; + int64_t largest_scid_seq_num = 0; ret = xqc_parse_mp_retire_conn_id_frame(packet_in, &seq_num, &path_id); if (ret != XQC_OK) { @@ -1941,7 +1969,7 @@ xqc_process_mp_retire_conn_id_frame(xqc_connection_t *conn, xqc_packet_in_t *pac return -XQC_EPROTO; } - if (seq_num > largest_scid_seq_num) { + if (seq_num > (uint64_t)largest_scid_seq_num) { /* * Receipt of a RETIRE_CONNECTION_ID frame containing a sequence number * greater than any previously sent to the peer MUST be treated as a @@ -2026,6 +2054,97 @@ xqc_process_max_path_id_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in return ret; } + +xqc_int_t +xqc_process_path_blocked_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in) +{ + xqc_int_t ret = XQC_ERROR; + uint64_t max_path_id, new_max_path_id; + + ret = xqc_parse_path_blocked_frame(packet_in, &max_path_id); + if (ret != XQC_OK) { + xqc_log(conn->log, XQC_LOG_ERROR, + "|xqc_process_max_paths_frame error|"); + return ret; + } + + xqc_log(conn->log, XQC_LOG_DEBUG, + "|max_path_id:%ui|pre_local_max_path_id:%ui|create_path_count:%ui|max_paths_count:%ui|", + max_path_id, conn->local_max_path_id, + conn->create_path_count, conn->max_paths_count); + + if (conn->local_max_path_id < max_path_id) { + xqc_log(conn->log, XQC_LOG_ERROR, + "|invalid path blocked frame|"); + return -XQC_EIGNORE_PKT; + } + + if (conn->local_max_path_id > max_path_id) { + xqc_log(conn->log, XQC_LOG_DEBUG, + "|received out-dated path blocked frame|local_max_path_id:%ui|max_path_id:%ui|", conn->local_max_path_id, max_path_id); + return XQC_OK; + } + + if (xqc_conn_check_path_id_blocked(conn) /* check whether all path ids have been used */ + && conn->create_path_count < conn->max_paths_count) /* check whether touched path resource limit */ + { + ret = xqc_conn_update_max_path_id(conn); + } + + return ret; +} + + +xqc_int_t +xqc_process_path_cids_blocked_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in) +{ + xqc_int_t ret = XQC_ERROR; + uint64_t path_id, new_max_path_id; + + ret = xqc_parse_path_cids_blocked_frame(packet_in, &path_id); + if (ret != XQC_OK) { + xqc_log(conn->log, XQC_LOG_ERROR, + "|xqc_process_max_paths_frame error|"); + return ret; + } + + xqc_cid_set_inner_t* inner_set = xqc_get_path_cid_set(&conn->scid_set, path_id); + uint64_t scid_unused_count = 0; + if (inner_set) { + scid_unused_count = inner_set->unused_cnt; + } + + xqc_log(conn->log, XQC_LOG_DEBUG, + "|path_id:%ui|local_max_path_id:%ui|create_path_count:%ui|max_paths_count:%ui|scid_unused_count:%ui|", + path_id, conn->local_max_path_id, + conn->create_path_count, conn->max_paths_count, + scid_unused_count); + + if (conn->local_max_path_id < path_id) { + xqc_log(conn->log, XQC_LOG_ERROR, + "|invalid path cids blocked frame|path_id:%ui|", path_id); + return -XQC_EIGNORE_PKT; + } + + /* try to add one new cid for the path id */ + uint64_t unused_limit = 2; + if (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, 0, 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; + } + } + + return ret; +} + + #ifdef XQC_ENABLE_FEC uint32_t diff --git a/src/transport/xqc_frame.h b/src/transport/xqc_frame.h index bf7ca5af..29778446 100644 --- a/src/transport/xqc_frame.h +++ b/src/transport/xqc_frame.h @@ -36,6 +36,8 @@ typedef enum { XQC_FRAME_MP_NEW_CONNECTION_ID, XQC_FRAME_MP_RETIRE_CONNECTION_ID, XQC_FRAME_MAX_PATH_ID, + XQC_FRAME_PATH_BLOCKED, + XQC_FRAME_PATH_CIDS_BLOCKED, XQC_FRAME_PATH_FROZEN, XQC_FRAME_DATAGRAM, XQC_FRAME_Extension, @@ -73,6 +75,8 @@ typedef enum { XQC_FRAME_BIT_MP_NEW_CONNECTION_ID = 1ULL << XQC_FRAME_MP_NEW_CONNECTION_ID, XQC_FRAME_BIT_MP_RETIRE_CONNECTION_ID = 1ULL << XQC_FRAME_MP_RETIRE_CONNECTION_ID, XQC_FRAME_BIT_MAX_PATH_ID = 1ULL << XQC_FRAME_MAX_PATH_ID, + XQC_FRAME_BIT_PATH_BLOCKED = 1ULL << XQC_FRAME_PATH_BLOCKED, + XQC_FRAME_BIT_PATH_CIDS_BLOCKED = 1ULL << XQC_FRAME_PATH_CIDS_BLOCKED, XQC_FRAME_BIT_PATH_FROZEN = 1ULL << XQC_FRAME_PATH_FROZEN, XQC_FRAME_BIT_DATAGRAM = 1ULL << XQC_FRAME_DATAGRAM, XQC_FRAME_BIT_Extension = 1ULL << XQC_FRAME_Extension, @@ -180,4 +184,8 @@ xqc_int_t xqc_process_mp_retire_conn_id_frame(xqc_connection_t *conn, xqc_packet xqc_int_t xqc_process_max_path_id_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in); +xqc_int_t xqc_process_path_blocked_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in); + +xqc_int_t xqc_process_path_cids_blocked_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in); + #endif /* _XQC_FRAME_H_INCLUDED_ */ diff --git a/src/transport/xqc_frame_parser.c b/src/transport/xqc_frame_parser.c index 040b4ff0..8662f031 100644 --- a/src/transport/xqc_frame_parser.c +++ b/src/transport/xqc_frame_parser.c @@ -2444,12 +2444,11 @@ xqc_parse_ack_mp_frame(xqc_packet_in_t *packet_in, xqc_connection_t *conn, /* + * * PATH_ABANDON Frame { - * Type (i) = TBD-03, - * Path ID (i), - * Error Code (i), - * Reason Phrase Length (i), - * Reason Phrase (..), + * Type (i) = TBD-02 (experiments use 0x15228c05), + * Path Identifier (i), + * Error Code (i), * } * */ @@ -2464,15 +2463,15 @@ xqc_gen_path_abandon_frame(xqc_connection_t *conn, xqc_packet_out_t *packet_out, uint64_t frame_type; need = po_remained_size = 0; - - if (conn->conn_settings.multipath_version >= XQC_MULTIPATH_10) { - /* same frame type in 05 and 06 */ - frame_type = XQC_TRANS_FRAME_TYPE_MP_ABANDON; - } else { + xqc_log(conn->log, XQC_LOG_DEBUG, "|multipath_version|%ui|", conn->conn_settings.multipath_version); + + if (conn->conn_settings.multipath_version < XQC_MULTIPATH_10) { return -XQC_EMP_INVALID_MP_VERTION; } + frame_type = XQC_TRANS_FRAME_TYPE_MP_ABANDON; + uint64_t reason_len = 0; uint8_t *reason = NULL; @@ -2483,9 +2482,13 @@ xqc_gen_path_abandon_frame(xqc_connection_t *conn, xqc_packet_out_t *packet_out, need = xqc_vint_len(frame_type_bits) + xqc_vint_len(path_id_bits) - + xqc_vint_len(error_code_bits) - + xqc_vint_len(reason_len_bits) - + reason_len; + + xqc_vint_len(error_code_bits); + + /* only draft-10 use reason field */ + if (conn->conn_settings.multipath_version == XQC_MULTIPATH_10) { + need += xqc_vint_len(reason_len_bits) + + reason_len; + } po_remained_size = xqc_get_po_remained_size(packet_out); @@ -2506,14 +2509,17 @@ xqc_gen_path_abandon_frame(xqc_connection_t *conn, xqc_packet_out_t *packet_out, xqc_vint_write(dst_buf, error_code, error_code_bits, xqc_vint_len(error_code_bits)); dst_buf += xqc_vint_len(error_code_bits); - /* Reason Phrase Length (i) */ - xqc_vint_write(dst_buf, reason_len, reason_len_bits, xqc_vint_len(reason_len_bits)); - dst_buf += xqc_vint_len(reason_len_bits); + /* only draft-10 use reason field */ + if (conn->conn_settings.multipath_version == XQC_MULTIPATH_10) { + /* Reason Phrase Length (i) */ + xqc_vint_write(dst_buf, reason_len, reason_len_bits, xqc_vint_len(reason_len_bits)); + dst_buf += xqc_vint_len(reason_len_bits); - /* Reason Phrase (..) */ - if (reason_len > 0) { - xqc_memcpy(dst_buf, reason, reason_len); - dst_buf += reason_len; + /* Reason Phrase (..) */ + if (reason_len > 0) { + xqc_memcpy(dst_buf, reason, reason_len); + dst_buf += reason_len; + } } packet_out->po_frame_types |= XQC_FRAME_BIT_PATH_ABANDON; @@ -2523,7 +2529,7 @@ xqc_gen_path_abandon_frame(xqc_connection_t *conn, xqc_packet_out_t *packet_out, xqc_int_t xqc_parse_path_abandon_frame(xqc_packet_in_t *packet_in, - uint64_t *path_id, uint64_t *error_code) + uint64_t *path_id, uint64_t *error_code, uint64_t has_reason) { unsigned char *p = packet_in->pos; const unsigned char *end = packet_in->last; @@ -2552,16 +2558,18 @@ xqc_parse_path_abandon_frame(xqc_packet_in_t *packet_in, } p += vlen; - /* Reason Phrase Length (i) */ - vlen = xqc_vint_read(p, end, &reason_len); - if (vlen < 0) { - return -XQC_EVINTREAD; - } - p += vlen; + if (has_reason) { + /* Reason Phrase Length (i) */ + vlen = xqc_vint_read(p, end, &reason_len); + if (vlen < 0) { + return -XQC_EVINTREAD; + } + p += vlen; - /* Reason Phrase (..) */ - if (reason_len > 0) { - p += reason_len; + /* Reason Phrase (..) */ + if (reason_len > 0) { + p += reason_len; + } } packet_in->pos = p; @@ -2824,7 +2832,7 @@ xqc_parse_mp_new_conn_id_frame(xqc_packet_in_t *packet_in, packet_in->pi_frame_types |= XQC_FRAME_BIT_MP_NEW_CONNECTION_ID; - xqc_log_event(conn->log, TRA_FRAMES_PROCESSED, XQC_FRAME_NEW_CONNECTION_ID, new_cid, retire_prior_to); + xqc_log_event(conn->log, TRA_FRAMES_PROCESSED, XQC_FRAME_MP_NEW_CONNECTION_ID, new_cid, retire_prior_to); return XQC_OK; } @@ -2952,5 +2960,123 @@ xqc_parse_max_path_id_frame(xqc_packet_in_t *packet_in, uint64_t *max_path_id) packet_in->pi_frame_types |= XQC_FRAME_BIT_MAX_PATH_ID; + return XQC_OK; +} + +/* + * + * PATHS_BLOCKED Frame { + * Type (i) = TBD-08 (experiments use 0x15228c0d), + * Maximum Path Identifier (i), + * } + * Figure 10: MAX_PATH_ID_BLOCKED Frame Format + * + * */ +ssize_t +xqc_gen_path_blocked_frame(xqc_packet_out_t *packet_out, uint64_t max_path_id) +{ + unsigned char *dst_buf = packet_out->po_buf + packet_out->po_used_size; + const unsigned char *begin = dst_buf; + + /* write frame type */ + uint64_t frame_type = XQC_TRANS_FRAME_TYPE_PATH_BLOCKED; + unsigned frame_type_bits = xqc_vint_get_2bit(frame_type); + xqc_vint_write(dst_buf, frame_type, frame_type_bits, xqc_vint_len(frame_type_bits)); + dst_buf += xqc_vint_len(frame_type_bits); + + unsigned max_paths_bits = xqc_vint_get_2bit(max_path_id); + xqc_vint_write(dst_buf, max_path_id, max_paths_bits, xqc_vint_len(max_paths_bits)); + dst_buf += xqc_vint_len(max_paths_bits); + + packet_out->po_frame_types |= XQC_FRAME_BIT_PATH_BLOCKED; + + return dst_buf - begin; +} + +xqc_int_t +xqc_parse_path_blocked_frame(xqc_packet_in_t *packet_in, uint64_t *max_path_id) +{ + unsigned char *p = packet_in->pos; + const unsigned char *end = packet_in->last; + int vlen; + + /* frame type */ + uint64_t frame_type = 0; + vlen = xqc_vint_read(p, end, &frame_type); /* get frame_type */ + if (vlen < 0) { + return -XQC_EVINTREAD; + } + p += vlen; + + vlen = xqc_vint_read(p, end, max_path_id); + if (vlen < 0) { + return -XQC_EVINTREAD; + } + p += vlen; + + packet_in->pos = p; + + packet_in->pi_frame_types |= XQC_FRAME_BIT_PATH_BLOCKED; + + return XQC_OK; +} + + + +/* + * + * PATH_CIDS_BLOCKED Frame { + * Type (i) = TBD-09 (experiments use 0x15228c0e), + * Path Identifier (i), + * } + * Figure 11: PATH_CIDS_BLOCKED Frame Format + * + * */ +ssize_t +xqc_gen_path_cids_blocked_frame(xqc_packet_out_t *packet_out, uint64_t path_id) +{ + unsigned char *dst_buf = packet_out->po_buf + packet_out->po_used_size; + const unsigned char *begin = dst_buf; + + /* write frame type */ + uint64_t frame_type = XQC_TRANS_FRAME_TYPE_PATH_CIDS_BLOCKED; + unsigned frame_type_bits = xqc_vint_get_2bit(frame_type); + xqc_vint_write(dst_buf, frame_type, frame_type_bits, xqc_vint_len(frame_type_bits)); + dst_buf += xqc_vint_len(frame_type_bits); + + unsigned max_paths_bits = xqc_vint_get_2bit(path_id); + xqc_vint_write(dst_buf, path_id, max_paths_bits, xqc_vint_len(max_paths_bits)); + dst_buf += xqc_vint_len(max_paths_bits); + + packet_out->po_frame_types |= XQC_FRAME_BIT_PATH_CIDS_BLOCKED; + + return dst_buf - begin; +} + +xqc_int_t +xqc_parse_path_cids_blocked_frame(xqc_packet_in_t *packet_in, uint64_t *path_id) +{ + unsigned char *p = packet_in->pos; + const unsigned char *end = packet_in->last; + int vlen; + + /* frame type */ + uint64_t frame_type = 0; + vlen = xqc_vint_read(p, end, &frame_type); /* get frame_type */ + if (vlen < 0) { + return -XQC_EVINTREAD; + } + p += vlen; + + vlen = xqc_vint_read(p, end, path_id); + if (vlen < 0) { + return -XQC_EVINTREAD; + } + p += vlen; + + packet_in->pos = p; + + packet_in->pi_frame_types |= XQC_FRAME_BIT_PATH_CIDS_BLOCKED; + return XQC_OK; } \ No newline at end of file diff --git a/src/transport/xqc_frame_parser.h b/src/transport/xqc_frame_parser.h index 2a71fc7d..1a8375f6 100644 --- a/src/transport/xqc_frame_parser.h +++ b/src/transport/xqc_frame_parser.h @@ -24,6 +24,8 @@ #define XQC_TRANS_FRAME_TYPE_MP_NEW_CONN_ID 0x15228c09 #define XQC_TRANS_FRAME_TYPE_MP_RETIRE_CONN_ID 0x15228c0a #define XQC_TRANS_FRAME_TYPE_MAX_PATH_ID 0x15228c0c +#define XQC_TRANS_FRAME_TYPE_PATH_BLOCKED 0x15228c0d +#define XQC_TRANS_FRAME_TYPE_PATH_CIDS_BLOCKED 0x15228c0e #define XQC_TRANS_FRAME_TYPE_MP_FROZEN 0x15228cff /** @@ -143,7 +145,7 @@ ssize_t xqc_gen_path_abandon_frame(xqc_connection_t *conn, xqc_packet_out_t *packet_out, uint64_t path_id, uint64_t error_code); xqc_int_t xqc_parse_path_abandon_frame(xqc_packet_in_t *packet_in, - uint64_t *path_id, uint64_t *error_code); + uint64_t *path_id, uint64_t *error_code, uint64_t has_reason); ssize_t xqc_gen_path_status_frame(xqc_connection_t *conn, xqc_packet_out_t *packet_out, @@ -178,5 +180,11 @@ xqc_int_t xqc_parse_mp_retire_conn_id_frame(xqc_packet_in_t *packet_in, uint64_t ssize_t xqc_gen_max_path_id_frame(xqc_packet_out_t *packet_out, uint64_t max_path_id); xqc_int_t xqc_parse_max_path_id_frame(xqc_packet_in_t *packet_in, uint64_t *max_path_id); +ssize_t xqc_gen_path_blocked_frame(xqc_packet_out_t *packet_out, uint64_t max_path_id); +xqc_int_t xqc_parse_path_blocked_frame(xqc_packet_in_t *packet_in, uint64_t *max_path_id); + +ssize_t xqc_gen_path_cids_blocked_frame(xqc_packet_out_t *packet_out, uint64_t path_id); +xqc_int_t xqc_parse_path_cids_blocked_frame(xqc_packet_in_t *packet_in, uint64_t *path_id); + void xqc_try_process_fec_decode(xqc_connection_t *conn, xqc_int_t block_id); #endif /*_XQC_FRAME_PARSER_H_INCLUDED_*/ diff --git a/src/transport/xqc_multipath.c b/src/transport/xqc_multipath.c index 34a624c4..02ce60f2 100644 --- a/src/transport/xqc_multipath.c +++ b/src/transport/xqc_multipath.c @@ -75,7 +75,7 @@ xqc_path_create(xqc_connection_t *conn, xqc_cid_t *scid, xqc_cid_t *dcid, uint64 { xqc_path_ctx_t *path = NULL; - if (conn->create_path_count >= XQC_MAX_PATHS_COUNT) { + if (conn->create_path_count > xqc_min(conn->local_max_path_id, conn->remote_max_path_id) + 1) { xqc_log(conn->log, XQC_LOG_ERROR, "|too many paths|current maximum:%d|", XQC_MAX_PATHS_COUNT); return NULL; @@ -139,6 +139,8 @@ xqc_path_create(xqc_connection_t *conn, xqc_cid_t *scid, xqc_cid_t *dcid, uint64 xqc_cid_copy(&(path->path_dcid), dcid); } + xqc_cid_copy(&path->path_last_dcid, &path->path_dcid); + xqc_cid_set_update_state(&conn->dcid_set, path_id, XQC_CID_SET_USED); xqc_cid_set_update_state(&conn->scid_set, path_id, XQC_CID_SET_USED); @@ -413,6 +415,8 @@ xqc_conn_is_current_mp_version_supported(xqc_multipath_version_t mp_version) xqc_int_t ret; switch (mp_version) { case XQC_MULTIPATH_10: + case XQC_MULTIPATH_11: + case XQC_MULTIPATH_12: ret = XQC_OK; break; default: @@ -515,6 +519,7 @@ xqc_conn_close_path(xqc_engine_t *engine, const xqc_cid_t *scid, uint64_t closed return -XQC_EMP_NO_ACTIVE_PATH; } + path->path_err_code = XQC_PATH_APPLICATION_ABANDON; xqc_int_t ret = xqc_path_immediate_close(path); if (ret != XQC_OK) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_path_immediate_close error|%d|", ret); @@ -1487,4 +1492,187 @@ xqc_path_recent_loss_rate(xqc_path_ctx_t *path) } return xqc_max(loss_rate0, loss_rate1); +} + + +xqc_int_t +xqc_path_cid_rotate(xqc_path_ctx_t *path, uint64_t path_id) +{ + xqc_int_t ret = XQC_ERROR; + xqc_connection_t *conn = path->parent_conn; + xqc_cid_t new_dcid; + xqc_cid_inner_t *new_dcid_inner; + ret = xqc_get_unused_cid(&conn->dcid_set, &new_dcid, path_id); + + if (ret != XQC_OK) { + xqc_log(path->parent_conn->log, XQC_LOG_ERROR, "|xqc_get_unused_cid err|path_id:%ui|", path_id); + return -XQC_EMP_NO_AVAILABLE_CID_FOR_PATH; + } + + xqc_log(path->parent_conn->log, XQC_LOG_DEBUG, "|xqc_conn_trigger_cid_rotation_on_path|path_id:%ui|new_dcid:%s|old_dcid:%s|", + path_id, + xqc_dcid_str(conn->engine,&new_dcid), + xqc_scid_str(conn->engine,&path->path_dcid)); + + xqc_cid_copy(&path->path_last_dcid, &path->path_dcid); + xqc_cid_copy(&path->path_dcid, &new_dcid); + + return XQC_OK; +} + + +xqc_int_t +xqc_conn_trigger_cid_rotation_on_path(xqc_engine_t *engine, const xqc_cid_t *scid, uint64_t path_id) +{ + xqc_connection_t *conn = NULL; + xqc_path_ctx_t *path = NULL; + + xqc_log(engine->log, XQC_LOG_DEBUG, "|scid:%s|path_id:%ui|", xqc_scid_str(engine, scid), path_id); + + conn = xqc_engine_conns_hash_find(engine, scid, 's'); + if (!conn) { + xqc_log(engine->log, XQC_LOG_ERROR, "|can not find connection|"); + return -XQC_ECONN_NFOUND; + } + if (conn->conn_state >= XQC_CONN_STATE_CLOSING) { + return -XQC_CLOSING; + } + + /* check mp-support */ + if (!conn->enable_multipath) { + xqc_log(engine->log, XQC_LOG_WARN, + "|Multipath is not supported in connection|%p|", conn); + return -XQC_EMP_NOT_SUPPORT_MP; + } + + /* abandon path */ + path = xqc_conn_find_path_by_path_id(conn, path_id); + if (path == NULL) { + xqc_log(engine->log, XQC_LOG_WARN, + "|path is not found by path_id in connection|%p|%ui|", + conn, path_id); + return -XQC_EMP_PATH_NOT_FOUND; + } + + xqc_int_t ret = xqc_path_cid_rotate(path, path_id); + if (ret != XQC_OK) { + xqc_log(engine->log, XQC_LOG_WARN, + "|xqc_path_cid_rotate error|scid:%s|path_id:%ui|", xqc_scid_str(engine, scid), path_id); + } + + return ret; +} + + +xqc_int_t +xqc_path_last_cid_retirement(xqc_path_ctx_t *path, uint64_t path_id) +{ + xqc_connection_t *conn = path->parent_conn; + xqc_engine_t *engine = conn->engine; + + xqc_int_t ret = XQC_ERROR; + if (path->path_last_dcid.cid_seq_num == path->path_dcid.cid_seq_num) { + xqc_log(path->parent_conn->log, XQC_LOG_ERROR, "|retiring the same dcid which path is still using|path_id:%ui|new_dcid:%s|old_dcid:%s|", path_id, + xqc_dcid_str(engine, &path->path_dcid), + xqc_scid_str(engine, &path->path_last_dcid)); + return XQC_ERROR; + } + + xqc_log(path->parent_conn->log, XQC_LOG_DEBUG, "|xqc_conn_trigger_cid_retirement_on_path|path_id:%ui|new_dcid:%s|old_dcid:%s|old_seq:%ui|", path_id, + xqc_dcid_str(engine, &path->path_dcid), + xqc_scid_str(engine, &path->path_last_dcid), + path->path_last_dcid.cid_seq_num); + + ret = xqc_write_mp_retire_conn_id_frame_to_packet(path->parent_conn, path->path_last_dcid.cid_seq_num, path_id); + if (ret != XQC_OK) { + xqc_log(path->parent_conn->log, XQC_LOG_ERROR, "|xqc_write_retire_conn_id_frame_to_packet error|"); + return ret; + } + + xqc_cid_inner_t *last_dcid = xqc_get_inner_cid_by_seq(&conn->dcid_set, path->path_last_dcid.cid_seq_num, path_id); + if (last_dcid == NULL) { + xqc_log(path->parent_conn->log, XQC_LOG_ERROR, "|can't get inner cid by seq|path_id:%ui|seq:%ui|", path_id, path->path_last_dcid.cid_seq_num); + return ret; + } + + xqc_cid_switch_to_next_state(&conn->dcid_set, last_dcid, XQC_CID_RETIRED, path_id); + + + return XQC_OK; +} + + +/* trigger the last dcid to be retired */ +xqc_int_t +xqc_conn_trigger_cid_retirement_on_path(xqc_engine_t *engine, const xqc_cid_t *scid, uint64_t path_id) +{ + xqc_connection_t *conn = NULL; + xqc_path_ctx_t *path = NULL; + + xqc_log(engine->log, XQC_LOG_DEBUG, "|scid:%s|path_id:%ui|", xqc_scid_str(engine, scid), path_id); + + conn = xqc_engine_conns_hash_find(engine, scid, 's'); + if (!conn) { + xqc_log(engine->log, XQC_LOG_ERROR, "|can not find connection|"); + return -XQC_ECONN_NFOUND; + } + if (conn->conn_state >= XQC_CONN_STATE_CLOSING) { + return -XQC_CLOSING; + } + + /* check mp-support */ + if (!conn->enable_multipath) { + xqc_log(engine->log, XQC_LOG_WARN, + "|Multipath is not supported in connection|%p|", conn); + return -XQC_EMP_NOT_SUPPORT_MP; + } + + /* abandon path */ + path = xqc_conn_find_path_by_path_id(conn, path_id); + if (path == NULL) { + xqc_log(engine->log, XQC_LOG_WARN, + "|path is not found by path_id in connection|%p|%ui|", + conn, path_id); + return -XQC_EMP_PATH_NOT_FOUND; + } + + xqc_int_t ret = xqc_path_last_cid_retirement(path, path_id); + if (ret != XQC_OK) { + xqc_log(engine->log, XQC_LOG_WARN, + "|xqc_path_last_cid_retirement error|scid:%s|path_id:%ui|", xqc_scid_str(engine, scid), path_id); + } + + return ret; +} + +xqc_bool_t +xqc_conn_check_path_id_blocked(xqc_connection_t *conn) +{ + if (conn->create_path_count >= conn->local_max_path_id + 1) { + return XQC_TRUE; + } + + return XQC_FALSE; +} + +xqc_int_t +xqc_conn_update_max_path_id(xqc_connection_t *conn) +{ + xqc_int_t ret = XQC_OK; + uint64_t pre_max_path_id = conn->local_max_path_id; + conn->local_max_path_id += (conn->local_max_path_id + 1) / 2; + conn->local_max_path_id = xqc_min(conn->local_max_path_id, conn->max_paths_count); + if (xqc_conn_add_path_cid_sets(conn, pre_max_path_id + 1, conn->local_max_path_id) != XQC_OK) { + xqc_log(conn->log, XQC_LOG_ERROR, "|add_path_cid_sets_error|"); + return -XQC_EMALLOC; + } + + ret = xqc_write_max_path_id_to_packet(conn, conn->local_max_path_id); + if (ret != XQC_OK) { + xqc_log(conn->log, XQC_LOG_ERROR, + "|xqc_write_max_path_id_to_packet error|"); + return ret; + } + + return ret; } \ No newline at end of file diff --git a/src/transport/xqc_multipath.h b/src/transport/xqc_multipath.h index d387630b..249743bb 100644 --- a/src/transport/xqc_multipath.h +++ b/src/transport/xqc_multipath.h @@ -91,6 +91,9 @@ struct xqc_path_ctx_s { uint64_t path_id; /* path identifier */ xqc_cid_t path_scid; xqc_cid_t path_dcid; + xqc_cid_t path_last_dcid; + + xqc_multipath_error_t path_err_code; /* Path_address: 4-tuple */ unsigned char peer_addr[sizeof(struct sockaddr_in6)], @@ -255,6 +258,9 @@ double xqc_path_recent_loss_rate(xqc_path_ctx_t *path); double xqc_conn_recent_loss_rate(xqc_connection_t *conn); +xqc_bool_t xqc_conn_check_path_id_blocked(xqc_connection_t *conn); +xqc_int_t xqc_conn_update_max_path_id(xqc_connection_t *conn); + #endif /* XQC_MULTIPATH_H */ diff --git a/src/transport/xqc_packet_out.c b/src/transport/xqc_packet_out.c index 697f338f..e3437a71 100644 --- a/src/transport/xqc_packet_out.c +++ b/src/transport/xqc_packet_out.c @@ -1570,7 +1570,7 @@ xqc_write_path_abandon_frame_to_packet(xqc_connection_t *conn, xqc_path_ctx_t *p uint64_t path_id = path->path_id; - ret = xqc_gen_path_abandon_frame(conn, packet_out, path_id, 0); + ret = xqc_gen_path_abandon_frame(conn, packet_out, path_id, (uint64_t)path->path_err_code); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_path_abandon_frame error|%d|", ret); goto error; @@ -1802,7 +1802,7 @@ xqc_write_max_path_id_to_packet(xqc_connection_t *conn, uint64_t max_path_id) ret = xqc_gen_max_path_id_frame(packet_out, max_path_id); if (ret < 0) { - xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_max_streams_frame error|"); + xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_max_path_id_frame error|"); goto error; } packet_out->po_used_size += ret; @@ -1813,4 +1813,69 @@ xqc_write_max_path_id_to_packet(xqc_connection_t *conn, uint64_t max_path_id) error: xqc_maybe_recycle_packet_out(packet_out, conn); return -XQC_EWRITE_PKT; +} + + +int +xqc_write_path_blocked_to_packet(xqc_connection_t *conn, uint64_t max_path_id) +{ + ssize_t ret = XQC_ERROR; + xqc_packet_out_t *packet_out; + xqc_log(conn->log, XQC_LOG_DEBUG, "|path blocked max_path_id:%ui|", max_path_id); + + 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_blocked_frame(packet_out, max_path_id); + if (ret < 0) { + xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_path_blocked_frame error|"); + goto error; + } + packet_out->po_used_size += ret; + xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); + xqc_log(conn->log, XQC_LOG_DEBUG, "|max_path_id:%ui|", max_path_id); + return XQC_OK; + + error: + xqc_maybe_recycle_packet_out(packet_out, conn); + return -XQC_EWRITE_PKT; +} + + +int +xqc_write_path_cids_blocked_to_packet(xqc_connection_t *conn, uint64_t path_id) +{ + if (conn->conn_settings.multipath_version < XQC_MULTIPATH_12) { + /* old version, do nothing here */ + xqc_log(conn->log, XQC_LOG_DEBUG, "|xqc_write_path_cids_blocked_to_packet|old version:%ui|", + conn->conn_settings.multipath_version); + return XQC_OK; + } + + ssize_t ret = XQC_ERROR; + xqc_packet_out_t *packet_out; + xqc_log(conn->log, XQC_LOG_DEBUG, "|path_id:%ui|", path_id); + + 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_cids_blocked_frame(packet_out, path_id); + if (ret < 0) { + xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_path_blocked_frame error|"); + goto error; + } + packet_out->po_used_size += ret; + xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); + xqc_log(conn->log, XQC_LOG_DEBUG, "|max_path_id:%ui|", path_id); + return XQC_OK; + + error: + xqc_maybe_recycle_packet_out(packet_out, conn); + return -XQC_EWRITE_PKT; } \ No newline at end of file diff --git a/src/transport/xqc_packet_out.h b/src/transport/xqc_packet_out.h index c988920a..248a14f5 100644 --- a/src/transport/xqc_packet_out.h +++ b/src/transport/xqc_packet_out.h @@ -232,6 +232,8 @@ xqc_int_t xqc_write_mp_new_conn_id_frame_to_packet(xqc_connection_t *conn, uint6 xqc_int_t xqc_write_mp_retire_conn_id_frame_to_packet(xqc_connection_t *conn, uint64_t seq_num, uint64_t path_id); int xqc_write_max_path_id_to_packet(xqc_connection_t *conn, uint64_t max_path_id); +int xqc_write_path_blocked_to_packet(xqc_connection_t *conn, uint64_t max_path_id); +int xqc_write_path_cids_blocked_to_packet(xqc_connection_t *conn, uint64_t path_id); /** * @brief Get remained space size in packet out buff. diff --git a/src/transport/xqc_transport_params.c b/src/transport/xqc_transport_params.c index 53412c75..79f1c9cd 100644 --- a/src/transport/xqc_transport_params.c +++ b/src/transport/xqc_transport_params.c @@ -163,6 +163,15 @@ xqc_transport_params_calc_length(const xqc_transport_params_t *params, xqc_put_varint_len(xqc_put_varint_len(params->init_max_path_id)) + xqc_put_varint_len(params->init_max_path_id); + } else if (params->multipath_version == XQC_MULTIPATH_11) { + len += xqc_put_varint_len(XQC_TRANSPORT_PARAM_INIT_MAX_PATH_ID_V11) + + xqc_put_varint_len(xqc_put_varint_len(params->init_max_path_id)) + + xqc_put_varint_len(params->init_max_path_id); + + } else if (params->multipath_version == XQC_MULTIPATH_12) { + len += xqc_put_varint_len(XQC_TRANSPORT_PARAM_INIT_MAX_PATH_ID_V12) + + xqc_put_varint_len(xqc_put_varint_len(params->init_max_path_id)) + + xqc_put_varint_len(params->init_max_path_id); } } @@ -393,6 +402,11 @@ xqc_encode_transport_params(const xqc_transport_params_t *params, if (params->multipath_version == XQC_MULTIPATH_10) { p = xqc_put_varint_param(p, XQC_TRANSPORT_PARAM_INIT_MAX_PATH_ID_V10, params->init_max_path_id); + } else if (params->multipath_version == XQC_MULTIPATH_11) { + p = xqc_put_varint_param(p, XQC_TRANSPORT_PARAM_INIT_MAX_PATH_ID_V11, params->init_max_path_id); + + } else if (params->multipath_version == XQC_MULTIPATH_12) { + p = xqc_put_varint_param(p, XQC_TRANSPORT_PARAM_INIT_MAX_PATH_ID_V12, params->init_max_path_id); } } @@ -699,6 +713,16 @@ xqc_decode_enable_multipath(xqc_transport_params_t *params, xqc_transport_params params->multipath_version = XQC_MULTIPATH_10; XQC_DECODE_VINT_VALUE(¶ms->init_max_path_id, p, end); return XQC_OK; + } else if (param_type == XQC_TRANSPORT_PARAM_INIT_MAX_PATH_ID_V11) { + params->enable_multipath = 1; + params->multipath_version = XQC_MULTIPATH_11; + XQC_DECODE_VINT_VALUE(¶ms->init_max_path_id, p, end); + return XQC_OK; + } else if (param_type == XQC_TRANSPORT_PARAM_INIT_MAX_PATH_ID_V12) { + params->enable_multipath = 1; + params->multipath_version = XQC_MULTIPATH_12; + XQC_DECODE_VINT_VALUE(¶ms->init_max_path_id, p, end); + return XQC_OK; } return XQC_OK; } @@ -891,6 +915,8 @@ xqc_trans_param_get_index(uint64_t param_type) return param_type; case XQC_TRANSPORT_PARAM_INIT_MAX_PATH_ID_V10: + case XQC_TRANSPORT_PARAM_INIT_MAX_PATH_ID_V11: + case XQC_TRANSPORT_PARAM_INIT_MAX_PATH_ID_V12: return XQC_TRANSPORT_PARAM_ENABLE_MULTIPATH_PARSER; case XQC_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE: diff --git a/src/transport/xqc_transport_params.h b/src/transport/xqc_transport_params.h index 083c2ebe..c39b14b9 100644 --- a/src/transport/xqc_transport_params.h +++ b/src/transport/xqc_transport_params.h @@ -87,6 +87,8 @@ typedef enum { /* multipath quic attributes */ XQC_TRANSPORT_PARAM_INIT_MAX_PATH_ID_V10 = 0x0f739bbc1b666d09, + XQC_TRANSPORT_PARAM_INIT_MAX_PATH_ID_V11 = 0x0f739bbc1b666d11, + XQC_TRANSPORT_PARAM_INIT_MAX_PATH_ID_V12 = 0x0f739bbc1b666d0c, /* google connection options */ XQC_TRANSPORT_PARAM_GOOGLE_CO = 0x3128,