diff --git a/README.md b/README.md index 3425dfa42..797e84f3f 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ XQUIC Library released by Alibaba is … [![](https://img.shields.io/static/v1?label=draft-13&message=QUIC-LB&color=9cf)](https://tools.ietf.org/html/draft-ietf-quic-load-balancers-13) [![](https://img.shields.io/static/v1?label=draft-04&message=Multipath-QUIC&color=9cf)](https://tools.ietf.org/html/draft-ietf-quic-multipath-04) +[![](https://img.shields.io/static/v1?label=draft-05&message=Multipath-QUIC&color=9cf)](https://tools.ietf.org/html/draft-ietf-quic-multipath-05) #### Standardized Features diff --git a/demo/demo_client.c b/demo/demo_client.c index 4533b7fdc..64b59c2d8 100644 --- a/demo/demo_client.c +++ b/demo/demo_client.c @@ -116,6 +116,9 @@ typedef struct xqc_demo_cli_net_config_s { int multipath; + uint8_t rebind_p0; + uint8_t rebind_p1; + } xqc_demo_cli_net_config_t; /** @@ -162,6 +165,12 @@ typedef struct xqc_demo_cli_quic_config_s { uint8_t no_encryption; + uint64_t recv_rate; + + uint32_t reinjection; + + uint8_t mp_version; + } xqc_demo_cli_quic_config_t; @@ -232,9 +241,13 @@ typedef struct xqc_demo_cli_requests_s { /* delay X us to start reqs */ uint64_t req_start_delay; + uint64_t idle_gap; + /* serial requests */ uint8_t serial; + int throttled_req; + } xqc_demo_cli_requests_t; @@ -346,6 +359,7 @@ typedef struct xqc_demo_cli_user_path_s { uint8_t is_active; int fd; + int rebind_fd; struct sockaddr_in6 local_addr; socklen_t local_addrlen; @@ -353,6 +367,7 @@ typedef struct xqc_demo_cli_user_path_s { socklen_t peer_addrlen; struct event *ev_socket; + struct event *ev_rebind_socket; struct event *ev_timeout; uint64_t last_sock_op_time; @@ -372,12 +387,18 @@ typedef struct xqc_demo_cli_user_conn_s { int total_path_cnt; struct event *ev_delay_req; + struct event *ev_idle_restart; struct event *ev_close_path; + struct event *ev_rebinding_p0; + struct event *ev_rebinding_p1; xqc_demo_cli_ctx_t *ctx; xqc_demo_cli_task_t *task; } xqc_demo_cli_user_conn_t; +static void +xqc_demo_cli_delayed_idle_restart(int fd, short what, void *arg); + void xqc_demo_cli_continue_send_reqs(xqc_demo_cli_user_conn_t *user_conn); @@ -401,10 +422,21 @@ xqc_demo_cli_close_task(xqc_demo_cli_task_t *task) user_conn->paths[i].is_active = 0; user_conn->active_path_cnt--; /* remove event handle */ - event_del(user_conn->paths[i].ev_socket); + if (user_conn->paths[i].ev_socket) { + event_del(user_conn->paths[i].ev_socket); + } + event_del(user_conn->paths[i].ev_timeout); /* close socket */ close(user_conn->paths[i].fd); + + if (user_conn->paths[i].ev_rebind_socket) { + event_del(user_conn->paths[i].ev_rebind_socket); + } + + if (user_conn->paths[i].rebind_fd != -1) { + close(user_conn->paths[i].rebind_fd); + } } } @@ -413,11 +445,26 @@ xqc_demo_cli_close_task(xqc_demo_cli_task_t *task) user_conn->ev_delay_req = NULL; } + if (user_conn->ev_idle_restart) { + event_del(user_conn->ev_idle_restart); + user_conn->ev_idle_restart = NULL; + } + if (user_conn->ev_close_path) { event_del(user_conn->ev_close_path); user_conn->ev_close_path = NULL; } + if (user_conn->ev_rebinding_p0) { + event_del(user_conn->ev_rebinding_p0); + user_conn->ev_rebinding_p0 = NULL; + } + + if (user_conn->ev_rebinding_p1) { + event_del(user_conn->ev_rebinding_p1); + user_conn->ev_rebinding_p1 = NULL; + } + return 0; } @@ -449,13 +496,6 @@ xqc_demo_cli_on_stream_fin(xqc_demo_cli_user_stream_t *user_stream) printf("task[%d], fin_cnt: %d, fin_flag: %d\n", task_idx, ctx->schedule.schedule_info[task_idx].req_fin_cnt, ctx->schedule.schedule_info[task_idx].fin_flag); - - /* TODO: fix MAX_STREAMS */ - if (ctx->schedule.schedule_info[task_idx].req_create_cnt - < ctx->tasks[task_idx].user_conn->task->req_cnt) - { - xqc_demo_cli_continue_send_reqs(ctx->tasks[task_idx].user_conn); - } } /* directly finish a task */ @@ -685,10 +725,12 @@ xqc_demo_cli_write_socket_ex(uint64_t path_id, const unsigned char *buf, size_t } if (user_path == NULL) { - printf("path %"PRIu64" is not avaliable!\n", path_id); + // printf("path %"PRIu64" is not avaliable!\n", path_id); return XQC_SOCKET_ERROR; } + // printf("path %"PRIu64" with fd:%d\n", path_id, user_path->fd); + do { errno = 0; res = sendto(user_path->fd, buf, size, 0, peer_addr, peer_addrlen); @@ -760,14 +802,25 @@ xqc_demo_cli_conn_create_path(const xqc_cid_t *cid, void *conn_user_data) xqc_demo_cli_ctx_t *ctx = user_conn->ctx; uint64_t path_id; int ret; + int backup = 0; if (user_conn->total_path_cnt < ctx->args->net_cfg.ifcnt && user_conn->total_path_cnt < MAX_PATH_CNT) { - ret = xqc_conn_create_path(ctx->engine, &(user_conn->cid), &path_id); + + 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\n", ret); return; } + + if (backup == 1) { + printf("Init No.%d path (id = %"PRIu64") to STANDBY state\n", 1, path_id); + } + ret = xqc_demo_cli_init_user_path(user_conn, user_conn->total_path_cnt, path_id); if (ret < 0) { xqc_conn_close_path(ctx->engine, &(user_conn->cid), path_id); @@ -795,10 +848,21 @@ xqc_demo_cli_path_removed(const xqc_cid_t *scid, uint64_t path_id, user_conn->paths[i].is_active = 0; user_conn->active_path_cnt--; /* remove event handle */ - event_del(user_conn->paths[i].ev_socket); + if (user_conn->paths[i].ev_socket) { + event_del(user_conn->paths[i].ev_socket); + } event_del(user_conn->paths[i].ev_timeout); /* close socket */ close(user_conn->paths[i].fd); + + if (user_conn->paths[i].ev_rebind_socket) { + event_del(user_conn->paths[i].ev_rebind_socket); + } + + if (user_conn->paths[i].rebind_fd != -1) { + close(user_conn->paths[i].rebind_fd); + } + printf("No.%d path removed id = %"PRIu64"\n", i, path_id); } } @@ -1013,6 +1077,9 @@ xqc_demo_cli_h3_request_read_notify(xqc_h3_request_t *h3_request, xqc_request_no DEBUG; unsigned char fin = 0; xqc_demo_cli_user_stream_t *user_stream = (xqc_demo_cli_user_stream_t *) user_data; + xqc_demo_cli_task_ctx_t *ctx = &user_stream->user_conn->ctx->task_ctx; + xqc_demo_cli_user_conn_t *user_conn = user_stream->user_conn; + uint32_t task_idx = user_conn->task->task_idx; // printf("xqc_demo_cli_h3_request_read_notify, h3_request: %p, user_stream: %p\n", h3_request, user_stream); if (flag & XQC_REQ_NOTIFY_READ_HEADER) { xqc_http_headers_t *headers; @@ -1086,6 +1153,26 @@ xqc_demo_cli_h3_request_read_notify(xqc_h3_request_t *h3_request, xqc_request_no user_stream->recv_body_fp = NULL; } // xqc_demo_cli_on_stream_fin(user_stream); + task_idx = user_stream->user_conn->task->task_idx; + if (ctx->schedule.schedule_info[task_idx].req_create_cnt + < ctx->tasks[task_idx].user_conn->task->req_cnt) + { + if (user_conn->ctx->args->req_cfg.idle_gap) { + + user_conn->ev_idle_restart = event_new(user_conn->ctx->eb, -1, 0, + xqc_demo_cli_delayed_idle_restart, + user_conn); + struct timeval tv = { + .tv_sec = user_conn->ctx->args->req_cfg.idle_gap / 1000, + .tv_usec = (user_conn->ctx->args->req_cfg.idle_gap % 1000) * 1000, + }; + event_add(user_conn->ev_idle_restart, &tv); + + } else { + xqc_demo_cli_continue_send_reqs(ctx->tasks[task_idx].user_conn); + } + + } } return 0; @@ -1113,8 +1200,10 @@ xqc_demo_cli_h3_request_close_notify(xqc_h3_request_t *h3_request, void *user_da xqc_request_stats_t stats; stats = xqc_h3_request_get_stats(h3_request); printf("send_body_size:%zu, recv_body_size:%zu, send_header_size:%zu, recv_header_size:%zu, " - "recv_fin:%d, err:%d\n", stats.send_body_size, stats.recv_body_size, - stats.send_header_size, stats.recv_header_size, user_stream->recv_fin, stats.stream_err); + "recv_fin:%d, err:%d, rate_limit:%"PRIu64", mp_state:%d, avail_send_weight:%.3lf, avail_recv_weight:%.3lf\n", + stats.send_body_size, stats.recv_body_size, stats.send_header_size, stats.recv_header_size, + user_stream->recv_fin, stats.stream_err, stats.rate_limit, stats.mp_state, + stats.mp_default_path_send_weight, stats.mp_default_path_recv_weight); printf("\033[33m[H3-req] send_bytes:%zu, recv_bytes:%zu, path_info:%s\n\033[0m", stats.send_body_size + stats.send_header_size, @@ -1160,6 +1249,8 @@ xqc_demo_cli_socket_read_handler(xqc_demo_cli_user_conn_t *user_conn, int fd) return; } + // printf("socket read: path%"PRIu64" fd:%d\n", user_path->path_id, user_path->fd); + do { recv_size = recvfrom(user_path->fd, packet_buf, sizeof(packet_buf), 0, (struct sockaddr *)&addr, &addr_len); @@ -1181,11 +1272,19 @@ xqc_demo_cli_socket_read_handler(xqc_demo_cli_user_conn_t *user_conn, int fd) recv_sum += recv_size; uint64_t recv_time = xqc_demo_now(); user_path->last_sock_op_time = recv_time; +#ifdef XQC_NO_PID_PACKET_PROCESS if (xqc_engine_packet_process(user_conn->ctx->engine, packet_buf, recv_size, (struct sockaddr *)(&user_path->local_addr), user_path->local_addrlen, (struct sockaddr *)(&addr), addr_len, (xqc_msec_t)recv_time, user_conn) != XQC_OK) +#else + if (xqc_engine_packet_process(user_conn->ctx->engine, packet_buf, recv_size, + (struct sockaddr *)(&user_path->local_addr), + user_path->local_addrlen, (struct sockaddr *)(&addr), + addr_len, user_path->path_id, (xqc_msec_t)recv_time, + user_conn) != XQC_OK) +#endif { return; } @@ -1204,8 +1303,10 @@ xqc_demo_cli_socket_event_callback(int fd, short what, void *arg) if (what & EV_WRITE) { xqc_demo_cli_socket_write_handler(user_conn, fd); + } else if (what & EV_READ) { xqc_demo_cli_socket_read_handler(user_conn, fd); + } else { printf("event callback: what=%d\n", what); exit(1); @@ -1260,11 +1361,26 @@ xqc_demo_cli_idle_callback(int fd, short what, void *arg) user_conn->ev_delay_req = NULL; } + if (user_conn->ev_idle_restart) { + event_del(user_conn->ev_idle_restart); + user_conn->ev_idle_restart = NULL; + } + if (user_conn->ev_close_path) { event_del(user_conn->ev_close_path); user_conn->ev_close_path = NULL; } + if (user_conn->ev_rebinding_p0) { + event_del(user_conn->ev_rebinding_p0); + user_conn->ev_rebinding_p0 = NULL; + } + + if (user_conn->ev_rebinding_p1) { + event_del(user_conn->ev_rebinding_p1); + user_conn->ev_rebinding_p1 = NULL; + } + printf("socket idle timeout, task failed, total task_cnt: %d, req_fin_cnt: %d, req_sent_cnt: %d, req_create_cnt: %d\n", user_conn->ctx->task_ctx.tasks[user_conn->task->task_idx].req_cnt, user_conn->ctx->task_ctx.schedule.schedule_info[user_conn->task->task_idx].req_fin_cnt, @@ -1292,6 +1408,15 @@ xqc_demo_cli_delayed_req_start(int fd, short what, void *arg) user_conn->task->reqs, req_cnt); } +static void +xqc_demo_cli_delayed_idle_restart(int fd, short what, void *arg) +{ + xqc_demo_cli_user_conn_t *user_conn = (xqc_demo_cli_user_conn_t *)arg; + xqc_demo_cli_task_ctx_t *ctx = &user_conn->ctx->task_ctx; + int task_idx = user_conn->task->task_idx; + xqc_demo_cli_continue_send_reqs(ctx->tasks[task_idx].user_conn); +} + static void xqc_demo_cli_close_path_timeout(int fd, short what, void *arg) { @@ -1302,6 +1427,41 @@ xqc_demo_cli_close_path_timeout(int fd, short what, void *arg) } } +static void +xqc_demo_cli_rebind_path0(int fd, short what, void *arg) +{ + xqc_demo_cli_user_conn_t *user_conn = (xqc_demo_cli_user_conn_t *) arg; + if (user_conn->paths[0].is_active) { + // change fd + int temp = user_conn->paths[0].fd; + user_conn->paths[0].fd = user_conn->paths[0].rebind_fd; + user_conn->paths[0].rebind_fd = user_conn->paths[0].fd; + + //stop read from the old socket + event_del(user_conn->paths[0].ev_socket); + user_conn->paths[0].ev_socket = NULL; + + xqc_h3_conn_send_ping(user_conn->ctx->engine, &user_conn->cid, NULL); + } +} + +static void +xqc_demo_cli_rebind_path1(int fd, short what, void *arg) +{ + xqc_demo_cli_user_conn_t *user_conn = (xqc_demo_cli_user_conn_t *) arg; + if (user_conn->paths[1].is_active) { + // change fd + int temp = user_conn->paths[1].fd; + user_conn->paths[1].fd = user_conn->paths[1].rebind_fd; + user_conn->paths[1].rebind_fd = user_conn->paths[1].fd; + + event_del(user_conn->paths[1].ev_socket); + user_conn->paths[1].ev_socket = NULL; + + xqc_h3_conn_send_ping(user_conn->ctx->engine, &user_conn->cid, NULL); + } +} + /****************************************************************************** * start of client init functions * @@ -1388,6 +1548,9 @@ xqc_demo_cli_init_conneciton_settings(xqc_conn_settings_t* settings, if (strncmp(args->quic_cfg.mp_sched, "minrtt", strlen("minrtt")) == 0) { sched = xqc_minrtt_scheduler_cb; + } if (strncmp(args->quic_cfg.mp_sched, "backup", strlen("backup")) == 0) { + sched = xqc_backup_scheduler_cb; + } else { #ifdef XQC_ENABLE_MP_INTEROP sched = xqc_interop_scheduler_cb; @@ -1406,6 +1569,16 @@ xqc_demo_cli_init_conneciton_settings(xqc_conn_settings_t* settings, settings->enable_multipath = args->net_cfg.multipath; settings->mp_ack_on_any_path = args->quic_cfg.mp_ack_on_any_path; settings->scheduler_callback = sched; + settings->recv_rate_bytes_per_sec = args->quic_cfg.recv_rate; + settings->mp_enable_reinjection = args->quic_cfg.reinjection; + settings->reinj_ctl_callback = xqc_deadline_reinj_ctl_cb; + settings->standby_path_probe_timeout = 1000; + settings->multipath_version = args->quic_cfg.mp_version; + settings->mp_ping_on = 1; + if (args->req_cfg.throttled_req != -1) { + settings->enable_stream_rate_limit = 1; + settings->recv_rate_bytes_per_sec = 0; + } } /* set client args to default values */ @@ -1429,6 +1602,10 @@ xqc_demo_cli_init_args(xqc_demo_cli_client_args_t *args) args->quic_cfg.alpn_type = ALPN_HQ; strncpy(args->quic_cfg.alpn, "hq-interop", sizeof(args->quic_cfg.alpn)); args->quic_cfg.keyupdate_pkt_threshold = UINT64_MAX; + /* default 04 */ + args->quic_cfg.mp_version = XQC_MULTIPATH_04; + + args->req_cfg.throttled_req = -1; } @@ -1532,20 +1709,26 @@ xqc_demo_cli_usage(int argc, char *argv[]) " -i interface to create a path. For instance, we can use '-i lo -i lo' to create two paths via lo.\n" " -w waiting N ms to start the first request.\n" " -P enable MPQUIC to return ACK_MPs on any paths.\n" - " -s multipath scheduler (interop, minrtt), default: interop\n" + " -s multipath scheduler (interop, minrtt, backup), default: interop\n" " -b set the second path as a backup path\n" " -Z close one path after X ms\n" " -N No encryption (default disabled)\n" " -Q Send requests one by one (default disabled)\n" + " -T Throttle recving rate (Bps)\n" + " -R Reinjection (1,2,4) \n" + " -V Multipath Version (4,5)\n" + " -I Idle interval between requests (ms)\n" + " -n Throttling the {1,2,...}xn-th requests\n" + " -e NAT rebinding on path 0\n" + " -E NAT rebinding on path 1\n" , prog); } - 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:dMi:w:Ps:bZ:NQ")) != -1) { + while ((ch = getopt(argc, argv, "a:p:c:Ct:S:0m:A:D:l:L:k:K:U:u:dMi:w:Ps:bZ:NQT:R:V:I:n:eE")) != -1) { switch (ch) { /* server ip */ case 'a': @@ -1732,6 +1915,41 @@ xqc_demo_cli_parse_args(int argc, char *argv[], xqc_demo_cli_client_args_t *args args->req_cfg.serial = 1; break; + case 'T': + printf("option recv rate limit: %s\n", optarg); + args->quic_cfg.recv_rate = atoi(optarg); + break; + + case 'R': + printf("option reinjection: %s\n", optarg); + args->quic_cfg.reinjection = atoi(optarg); + break; + + case 'V': + printf("option multipath version: %s\n", optarg); + args->quic_cfg.mp_version = atoi(optarg); + break; + + case 'I': + printf("option idle gap: %s\n", optarg); + args->req_cfg.idle_gap = atoi(optarg); + break; + + case 'n': + printf("option throttled reqs: %s\n", optarg); + args->req_cfg.throttled_req = atoi(optarg); + break; + + case 'e': + printf("option rebinding path0 after 2s\n"); + args->net_cfg.rebind_p0 = 1; + break; + + case 'E': + printf("option rebinding path1 after 3s\n"); + args->net_cfg.rebind_p1 = 1; + break; + default: printf("other option :%c\n", ch); xqc_demo_cli_usage(argc, argv); @@ -1812,8 +2030,24 @@ int xqc_demo_cli_send_h3_req(xqc_demo_cli_user_conn_t *user_conn, xqc_demo_cli_user_stream_t *user_stream, xqc_demo_cli_request_t *req) { + xqc_stream_settings_t settings = { .recv_rate_bytes_per_sec = 0 }; + int task_idx = user_conn->task->task_idx; + int req_create_cnt = user_conn->ctx->task_ctx.schedule.schedule_info[task_idx].req_create_cnt; + if (user_conn->ctx->args->req_cfg.throttled_req != -1) { + if (req_create_cnt == user_conn->ctx->args->req_cfg.throttled_req) { + settings.recv_rate_bytes_per_sec = user_conn->ctx->args->quic_cfg.recv_rate; + } + + if (req_create_cnt != 0 + && user_conn->ctx->args->req_cfg.throttled_req != 0 + && (req_create_cnt % user_conn->ctx->args->req_cfg.throttled_req) == 0) + { + settings.recv_rate_bytes_per_sec = user_conn->ctx->args->quic_cfg.recv_rate; + } + } + user_stream->h3_request = xqc_h3_request_create(user_conn->ctx->engine, &user_conn->cid, - user_stream); + &settings, user_stream); if (user_stream->h3_request == NULL) { printf("xqc_h3_request_create error\n"); return -1; @@ -1938,9 +2172,9 @@ xqc_demo_cli_h3_conn_close_notify(xqc_h3_conn_t *h3_conn, const xqc_cid_t *cid, xqc_demo_cli_user_conn_t *user_conn = (xqc_demo_cli_user_conn_t *)user_data; xqc_conn_stats_t stats = xqc_conn_get_stats(user_conn->ctx->engine, cid); printf("send_count:%u, lost_count:%u, tlp_count:%u, recv_count:%u, srtt:%"PRIu64" " - "early_data_flag:%d, conn_err:%d, ack_info:%s\n", stats.send_count, stats.lost_count, + "early_data_flag:%d, conn_err:%d, ack_info:%s conn_info:%s\n", stats.send_count, stats.lost_count, stats.tlp_count, stats.recv_count, stats.srtt, stats.early_data_flag, stats.conn_err, - stats.ack_info); + stats.ack_info, stats.conn_info); xqc_demo_cli_on_task_finish(user_conn->ctx, user_conn->task); free(user_conn); @@ -2176,6 +2410,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->net_cfg.rebind_p0) { + user_conn->ev_rebinding_p0 = event_new(user_conn->ctx->eb, -1, 0, + xqc_demo_cli_rebind_path0, + user_conn); + struct timeval tv = { + .tv_sec = 2, + .tv_usec = 0, + }; + event_add(user_conn->ev_rebinding_p0, &tv); + } + + if (args->net_cfg.rebind_p1) { + user_conn->ev_rebinding_p1 = event_new(user_conn->ctx->eb, -1, 0, + xqc_demo_cli_rebind_path1, + user_conn); + struct timeval tv = { + .tv_sec = 3, + .tv_usec = 0, + }; + event_add(user_conn->ev_rebinding_p1, &tv); + } + if (args->req_cfg.req_start_delay) { user_conn->ev_delay_req = event_new(user_conn->ctx->eb, -1, 0, xqc_demo_cli_delayed_req_start, @@ -2319,11 +2575,36 @@ xqc_demo_cli_init_user_path(xqc_demo_cli_user_conn_t *user_conn, int path_seq, u return -1; } + user_path->rebind_fd = -1; + user_path->ev_rebind_socket = NULL; + + if (ctx->args->net_cfg.rebind_p0 && path_seq == 0) { + user_path->rebind_fd = xqc_demo_cli_create_socket(user_path, &ctx->args->net_cfg, path_seq); + if (user_path->rebind_fd < 0) { + printf("xqc_create_rebind_socket error\n"); + return -1; + } + } + + if (ctx->args->net_cfg.rebind_p1 && path_seq == 1) { + user_path->rebind_fd = xqc_demo_cli_create_socket(user_path, &ctx->args->net_cfg, path_seq); + if (user_path->rebind_fd < 0) { + printf("xqc_create_rebind_socket error\n"); + return -1; + } + } + /* socket event */ user_path->ev_socket = event_new(ctx->eb, user_path->fd, EV_READ | EV_PERSIST, xqc_demo_cli_socket_event_callback, user_conn); event_add(user_path->ev_socket, NULL); + if (user_path->rebind_fd != -1) { + user_path->ev_rebind_socket = event_new(ctx->eb, user_path->rebind_fd, EV_READ | EV_PERSIST, + xqc_demo_cli_socket_event_callback, user_conn); + event_add(user_path->ev_rebind_socket, NULL); + } + /* xquic timer */ user_path->ev_timeout = event_new(ctx->eb, -1, 0, xqc_demo_cli_idle_callback, user_path); struct timeval tv; diff --git a/demo/demo_server.c b/demo/demo_server.c index 8075da059..372a4f8d6 100644 --- a/demo/demo_server.c +++ b/demo/demo_server.c @@ -93,6 +93,8 @@ typedef struct xqc_demo_svr_quic_config_s { /* scheduler */ char mp_sched[32]; + uint32_t reinjection; + } xqc_demo_svr_quic_config_t; @@ -354,8 +356,8 @@ void xqc_demo_svr_conn_update_cid_notify(xqc_connection_t *conn, const xqc_cid_t *retire_cid, const xqc_cid_t *new_cid, void *user_data) { - xqc_demo_svr_user_conn_t *user_conn = (xqc_demo_svr_user_conn_t *)user_data; - memcpy(&user_conn->cid, new_cid, sizeof(*new_cid)); + // xqc_demo_svr_user_conn_t *user_conn = (xqc_demo_svr_user_conn_t *)user_data; + // memcpy(&user_conn->cid, new_cid, sizeof(*new_cid)); } /****************************************************************************** @@ -1021,10 +1023,17 @@ xqc_demo_svr_socket_read_handler(xqc_demo_svr_ctx_t *ctx, int fd) recv_sum += recv_size; uint64_t recv_time = xqc_demo_now(); +#ifdef XQC_NO_PID_PACKET_PROCESS xqc_int_t ret = xqc_engine_packet_process(ctx->engine, packet_buf, recv_size, (struct sockaddr *)(&ctx->local_addr), ctx->local_addrlen, (struct sockaddr *)(&peer_addr), peer_addrlen, (xqc_usec_t)recv_time, ctx); +#else + xqc_int_t ret = xqc_engine_packet_process(ctx->engine, packet_buf, recv_size, + (struct sockaddr *)(&ctx->local_addr), ctx->local_addrlen, + (struct sockaddr *)(&peer_addr), peer_addrlen, XQC_UNKNOWN_PATH_ID, + (xqc_usec_t)recv_time, ctx); +#endif if (ret != XQC_OK) { printf("server_read_handler: packet process err, ret: %d\n", ret); return; @@ -1167,7 +1176,8 @@ xqc_demo_svr_usage(int argc, char *argv[]) " -d do not read responses from files\n" " -M enable MPQUIC.\n" " -P enable MPQUIC to return ACK_MPs on any paths.\n" - " -s multipath scheduler (interop, minrtt), default: interop\n" + " -s multipath scheduler (interop, minrtt, backup), default: interop\n" + " -R Reinjection (1,2,4) \n" , prog); } @@ -1209,7 +1219,7 @@ void xqc_demo_svr_parse_args(int argc, char *argv[], xqc_demo_svr_args_t *args) { int ch = 0; - while ((ch = getopt(argc, argv, "p:c:CD:l:L:6k:rdMPs:")) != -1) { + while ((ch = getopt(argc, argv, "p:c:CD:l:L:6k:rdMPs:R:")) != -1) { switch (ch) { /* listen port */ case 'p': @@ -1299,6 +1309,11 @@ xqc_demo_svr_parse_args(int argc, char *argv[], xqc_demo_svr_args_t *args) strncpy(args->quic_cfg.mp_sched, optarg, 32); break; + case 'R': + printf("option reinjection: %s\n", optarg); + args->quic_cfg.reinjection = atoi(optarg); + break; + default: printf("other option :%c\n", ch); xqc_demo_svr_usage(argc, argv); @@ -1386,6 +1401,9 @@ xqc_demo_svr_init_conn_settings(xqc_demo_svr_args_t *args) if (strncmp(args->quic_cfg.mp_sched, "minrtt", strlen("minrtt")) == 0) { sched = xqc_minrtt_scheduler_cb; + } if (strncmp(args->quic_cfg.mp_sched, "backup", strlen("backup")) == 0) { + sched = xqc_backup_scheduler_cb; + } else { #ifdef XQC_ENABLE_MP_INTEROP sched = xqc_interop_scheduler_cb; @@ -1405,6 +1423,9 @@ xqc_demo_svr_init_conn_settings(xqc_demo_svr_args_t *args) .enable_multipath = args->quic_cfg.multipath, .mp_ack_on_any_path = args->quic_cfg.mp_ack_on_any_path, .scheduler_callback = sched, + .reinj_ctl_callback = xqc_deadline_reinj_ctl_cb, + .mp_enable_reinjection = args->quic_cfg.reinjection, + .standby_path_probe_timeout = 1000, }; xqc_server_set_conn_settings(&conn_settings); diff --git a/demo/xqc_hq_request.c b/demo/xqc_hq_request.c index 19a36a929..4c655ee4b 100644 --- a/demo/xqc_hq_request.c +++ b/demo/xqc_hq_request.c @@ -85,7 +85,7 @@ xqc_hq_request_create(xqc_engine_t *engine, xqc_hq_conn_t *hqc, const xqc_cid_t } /* create stream, make hqr the user_data of xqc_stream_t */ - stream = xqc_stream_create(engine, cid, hqr); + stream = xqc_stream_create(engine, cid, NULL, hqr); if (NULL == stream) { PRINT_LOG("create transport-level stream error"); goto fail; @@ -344,30 +344,37 @@ xqc_hq_request_get_stats(xqc_hq_request_t *hqr) char *buff = stats.stream_info; size_t buff_size = XQC_STREAM_INFO_LEN; size_t cursor = 0, ret = 0; + int i; for (int i = 0; i < XQC_MAX_PATHS_COUNT; ++i) { if ((stream->paths_info[i].path_send_bytes > 0) || (stream->paths_info[i].path_recv_bytes > 0)) { - /* check buffer size */ - if (cursor + 100 >= buff_size) { - break; - } ret = snprintf(buff + cursor, buff_size - cursor, - "#%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64, + "%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64"#", stream->paths_info[i].path_id, stream->paths_info[i].path_pkt_send_count, stream->paths_info[i].path_pkt_recv_count, stream->paths_info[i].path_send_bytes, - stream->paths_info[i].path_send_reinject_bytes, - stream->paths_info[i].path_recv_bytes, - stream->paths_info[i].path_recv_reinject_bytes, - stream->paths_info[i].path_recv_effective_bytes, - stream->paths_info[i].path_recv_effective_reinject_bytes); + stream->paths_info[i].path_recv_bytes); cursor += ret; + + if (cursor >= buff_size) { + goto full; + } + } + } + +full: + cursor = xqc_min(cursor, buff_size); + for (i = cursor - 1; i >= 0; i--) { + if (buff[i] == '-' || buff[i] == '#') { + buff[i] = '\0'; + break; } } + buff[buff_size - 1] = '\0'; return stats; } diff --git a/include/xquic/xqc_errno.h b/include/xquic/xqc_errno.h index c26ddfd25..ea20d7051 100644 --- a/include/xquic/xqc_errno.h +++ b/include/xquic/xqc_errno.h @@ -26,6 +26,8 @@ typedef enum { TRA_0RTT_TRANS_PARAMS_ERROR = 0xE, /* MUST delete the current saved 0RTT transport parameters */ TRA_HS_CERTIFICATE_VERIFY_FAIL = 0x1FE, /* for handshake certificate verify error */ TRA_CRYPTO_ERROR = 0x1FF, /* 0x1XX */ + TRA_MP_PROTOCOL_VIOLATION_04 = 0xba01, + TRA_MP_PROTOCOL_VIOLATION_05 = 0x1001d76d3ded42f3, } xqc_trans_err_code_t; #define TRA_CRYPTO_ERROR_BASE 0x100 @@ -119,6 +121,7 @@ typedef enum { XQC_EMP_PATH_STATE_ERROR = 654, /* Multipath - abnormal path status */ XQC_EMP_SCHEDULE_PATH = 655, /* Multipath - fail to schedule path for sending */ XQC_EMP_NO_ACTIVE_PATH = 656, /* Multipath - no another active path */ + XQC_EMP_INVALID_MP_VERTION = 657, /* Multipath - the multipath version value is invalid */ XQC_EENCRYPT_LB_CID = 670, /* load balance connection ID encryption error */ XQC_EENCRYPT_AES_128_ECB = 671, /* aes_128_ecb algorithm error */ diff --git a/include/xquic/xqc_http3.h b/include/xquic/xqc_http3.h index e78d4da5a..f14cae4e1 100644 --- a/include/xquic/xqc_http3.h +++ b/include/xquic/xqc_http3.h @@ -114,7 +114,7 @@ typedef struct xqc_http_headers_s { } xqc_http_headers_t; -#define XQC_STREAM_INFO_LEN 400 +#define XQC_STREAM_INFO_LEN 100 /** * @brief request statistics structure @@ -122,8 +122,10 @@ typedef struct xqc_http_headers_s { typedef struct xqc_request_stats_s { size_t send_body_size; size_t recv_body_size; - size_t send_header_size; /* compressed header size */ - size_t recv_header_size; /* compressed header size */ + size_t send_header_size; /* plaintext header size */ + size_t recv_header_size; /* plaintext header size */ + size_t send_hdr_compressed; /* compressed header size */ + size_t recv_hdr_compressed; /* compressed header size */ int stream_err; /* QUIC layer error code, 0 for no error */ xqc_usec_t blocked_time; /* time of h3 stream being blocked */ xqc_usec_t unblocked_time; /* time of h3 stream being unblocked */ @@ -152,6 +154,8 @@ typedef struct xqc_request_stats_s { float mp_standby_path_send_weight; float mp_standby_path_recv_weight; + uint64_t rate_limit; + char stream_info[XQC_STREAM_INFO_LEN]; } xqc_request_stats_t; @@ -585,11 +589,12 @@ xqc_int_t xqc_h3_conn_set_qpack_dtable_cap(xqc_h3_conn_t *h3c, size_t capacity); * @param engine handler created by xqc_engine_create * @param cid connection id of http3 connection * @param user_data For request + * @param settings stream settings * @return handler of http3 request */ XQC_EXPORT_PUBLIC_API xqc_h3_request_t *xqc_h3_request_create(xqc_engine_t *engine, const xqc_cid_t *cid, - void *user_data); + xqc_stream_settings_t *settings, void *user_data); /** * @brief get statistics of a http3 request user can get it before request destroyed @@ -627,6 +632,10 @@ void xqc_h3_request_set_user_data(xqc_h3_request_t *h3_request, void *user_data) XQC_EXPORT_PUBLIC_API xqc_int_t xqc_h3_request_close(xqc_h3_request_t *h3_request); +XQC_EXPORT_PUBLIC_API +xqc_int_t xqc_h3_request_update_settings(xqc_h3_request_t *h3_request, + xqc_stream_settings_t *settings); + /** * @brief send http headers to peer * diff --git a/include/xquic/xquic.h b/include/xquic/xquic.h index 83906b8a7..77c9a21b2 100644 --- a/include/xquic/xquic.h +++ b/include/xquic/xquic.h @@ -417,17 +417,6 @@ typedef enum { XQC_PATH_RECOVERY, } xqc_path_status_change_type_t; -/** - * @brief multi-path quality callback function - * - * @param conn connection handler - * @param scid source connection id of endpoint - * @param path_id id of path - * @param conn_user_data user_data of connection - */ -typedef xqc_bool_t (*xqc_path_status_controller_pt)(xqc_connection_t *conn, - const xqc_cid_t *scid, xqc_path_status_change_type_t type, uint64_t path_id, - void *conn_user_data); /** * @brief multi-path write socket callback function @@ -663,11 +652,6 @@ typedef struct xqc_transport_callbacks_s { */ xqc_path_removed_notify_pt path_removed_notify; - /* - * Decision function for path status change. OPTIONAL for both client and server - */ - xqc_path_status_controller_pt path_status_controller; - /** * connection closing callback function. OPTIONAL for both client and server */ @@ -1142,6 +1126,13 @@ typedef struct xqc_linger_s { xqc_usec_t linger_timeout; /* 3*PTO if linger_timeout is 0 */ } xqc_linger_t; +typedef enum { + XQC_ERR_MULTIPATH_VERSION = 0x00, + XQC_MULTIPATH_04 = 0x04, + XQC_MULTIPATH_05 = 0x05, +} xqc_multipath_version_t; + + typedef struct xqc_conn_settings_s { int pacing_on; /* default: 0 */ int ping_on; /* client sends PING to keepalive, default:0 */ @@ -1173,11 +1164,14 @@ typedef struct xqc_conn_settings_s { /* * multipath option: - * https://datatracker.ietf.org/doc/html/draft-ietf-quic-multipath-04#section-3 + * https://datatracker.ietf.org/doc/html/draft-ietf-quic-multipath-05#section-3 * 0: don't support multipath * 1: supports multipath (unified solution) - multiple PN spaces */ uint64_t enable_multipath; + xqc_multipath_version_t multipath_version; + + /* * reinjection option: * 0: default, no reinjection @@ -1223,9 +1217,7 @@ typedef struct xqc_conn_settings_s { /* reinj_ctl callback, default: xqc_default_reinj_ctl_cb */ xqc_reinj_ctl_callback_t reinj_ctl_callback; - /* mark path unreachable if ctl_pto_count > path_unreachable_pto_count */ - uint64_t path_unreachable_pto_count; - + /* ms */ xqc_msec_t standby_path_probe_timeout; /* params for performance tuning */ @@ -1249,6 +1241,25 @@ typedef struct xqc_conn_settings_s { /* enable marking reinjected packets with reserved bits */ uint8_t marking_reinjection; + /* + * The limitation on conn recv rate (only applied to stream data) in bytes per second. + * NOTE: the minimal rate limitation is (63000/RTT) Bps. For instance, if RTT is 60ms, + * the minimal valid rate limitation is about 1MBps. Any recv_rate_bytes_per_sec less + * than the minimal valid rate limitation will not be guaranteed. + * default: 0 (no limitation). + */ + uint64_t recv_rate_bytes_per_sec; + + /* + * The switch to enable stream-level recv rate throttling. Default: off (0) + */ + uint8_t enable_stream_rate_limit; + + /* + * initial recv window. Default: 0 (use the internal default value) + */ + uint32_t init_recv_window; + } xqc_conn_settings_t; @@ -1275,6 +1286,9 @@ typedef struct xqc_path_metrics_s { uint64_t path_recv_reinject_bytes; uint64_t path_recv_effective_bytes; uint64_t path_recv_effective_reinject_bytes; + + uint64_t path_srtt; + uint8_t path_app_status; } xqc_path_metrics_t; @@ -1315,13 +1329,6 @@ typedef struct xqc_conn_stats_s { char alpn[XQC_MAX_ALPN_BUF_LEN]; } xqc_conn_stats_t; -typedef struct xqc_path_stats_s { - uint8_t get_stats_success; - xqc_usec_t last_tra_path_status_changed_time; - uint32_t send_count_since_last_tra_path_status_changed; - uint32_t pto_count_since_last_tra_path_status_changed; -} xqc_path_stats_t; - /************************************************************* * engine layer APIs @@ -1387,7 +1394,11 @@ xqc_int_t xqc_engine_unregister_alpn(xqc_engine_t *engine, const char *alpn, siz * Pass received UDP packet payload into xquic engine. * @param recv_time UDP packet received time in microsecond * @param user_data connection user_data, server is NULL + * @param path_id XQC_UNKNOWN_PATH_ID = unknown path (only use cid to identify the path) */ + +#ifdef XQC_NO_PID_PACKET_PROCESS + XQC_EXPORT_PUBLIC_API xqc_int_t xqc_engine_packet_process(xqc_engine_t *engine, const unsigned char *packet_in_buf, size_t packet_in_size, @@ -1395,6 +1406,17 @@ xqc_int_t xqc_engine_packet_process(xqc_engine_t *engine, const struct sockaddr *peer_addr, socklen_t peer_addrlen, xqc_usec_t recv_time, void *user_data); +#else + +XQC_EXPORT_PUBLIC_API +xqc_int_t xqc_engine_packet_process(xqc_engine_t *engine, + const unsigned char *packet_in_buf, size_t packet_in_size, + const struct sockaddr *local_addr, socklen_t local_addrlen, + const struct sockaddr *peer_addr, socklen_t peer_addrlen, + uint64_t path_id, xqc_usec_t recv_time, void *user_data); + +#endif + /** * @brief Process all connections, application implements MUST call this function in timer callback @@ -1614,7 +1636,8 @@ void xqc_conn_set_public_remote_trans_settings(xqc_connection_t *conn, * @param user_data user_data for this stream */ XQC_EXPORT_PUBLIC_API -xqc_stream_t *xqc_stream_create(xqc_engine_t *engine, const xqc_cid_t *cid, void *user_data); +xqc_stream_t *xqc_stream_create(xqc_engine_t *engine, + const xqc_cid_t *cid, xqc_stream_settings_t *settings, void *user_data); XQC_EXPORT_PUBLIC_API xqc_stream_t *xqc_stream_create_with_direction(xqc_connection_t *conn, @@ -1629,6 +1652,11 @@ xqc_stream_direction_t xqc_stream_get_direction(xqc_stream_t *strm); XQC_EXPORT_PUBLIC_API void xqc_stream_set_user_data(xqc_stream_t *stream, void *user_data); + +XQC_EXPORT_PUBLIC_API +xqc_int_t xqc_stream_update_settings(xqc_stream_t *stream, + xqc_stream_settings_t *settings); + /** * Get connection's user_data by stream */ @@ -1788,11 +1816,13 @@ xqc_conn_stats_t xqc_conn_get_stats(xqc_engine_t *engine, const xqc_cid_t *cid); * create new path for client * @param cid scid for connection * @param new_path_id if new path is created successfully, return new_path_id in this param + * @param path_status the initial status of the new path (1 = STANDBY, other values = AVAILABLE) * @return XQC_OK (0) when success, <0 for error */ XQC_EXPORT_PUBLIC_API xqc_int_t xqc_conn_create_path(xqc_engine_t *engine, - const xqc_cid_t *cid, uint64_t *new_path_id); + const xqc_cid_t *cid, uint64_t *new_path_id, + int path_status); /** @@ -1857,12 +1887,7 @@ 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); -/** - * User can get xqc_path_stats_t by path_id - */ -XQC_EXPORT_PUBLIC_API -xqc_path_stats_t xqc_path_get_stats(xqc_engine_t *engine, const xqc_cid_t *cid, - uint64_t path_id); + /** * @brief load balance cid encryption. diff --git a/include/xquic/xquic_typedef.h b/include/xquic/xquic_typedef.h index b02b6f2a4..d429fb1fa 100644 --- a/include/xquic/xquic_typedef.h +++ b/include/xquic/xquic_typedef.h @@ -152,6 +152,8 @@ typedef struct xqc_http_priority_s { /* max alpn buffer length */ #define XQC_MAX_ALPN_BUF_LEN 256 +#define XQC_UNKNOWN_PATH_ID ((uint64_t)-1) + typedef enum xqc_conn_settings_type_e { XQC_CONN_SETTINGS_DEFAULT, XQC_CONN_SETTINGS_LOW_DELAY, @@ -166,4 +168,8 @@ typedef struct xqc_conn_public_remote_trans_settings_s { uint16_t max_datagram_frame_size; } xqc_conn_public_remote_trans_settings_t; +typedef struct xqc_stream_settings_s { + uint64_t recv_rate_bytes_per_sec; +} xqc_stream_settings_t; + #endif /*_XQUIC_TYPEDEF_H_INCLUDED_*/ diff --git a/scripts/case_test.sh b/scripts/case_test.sh index d07469e2d..083e83219 100755 --- a/scripts/case_test.sh +++ b/scripts/case_test.sh @@ -1022,8 +1022,9 @@ fi clear_log echo -e "connection level flow control ...\c" -./test_client -s 512000 -l e -E -n 10 >> clog -if [[ `grep ">>>>>>>> pass:1" clog|wc -l` -eq 10 ]]; then +./test_client -s 512000 -l e -E -n 10 > stdlog +sleep 1 +if [[ `grep ">>>>>>>> pass:1" stdlog|wc -l` -eq 10 ]]; then echo ">>>>>>>> pass:1" case_print_result "connection_level_flow_control" "pass" else @@ -1565,6 +1566,37 @@ else fi grep_err_log +killall test_server +./test_server -l d -e -M > /dev/null & +sleep 1 + + +clear_log +echo -e "send 1M data on multiple paths with multipath vertion 04" +sudo ./test_client -s 1024000 -l d -t 1 -M -i lo -i lo -E -v 4 > stdlog +cli_result=`grep "multipath version negotiation succeed on multipath 04" clog` +if [ -n "$cli_result" ]; then + echo ">>>>>>>> pass:1" + case_print_result "MPNS_send_data_with_multipath_04" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "MPNS_send_data_with_multipath_04" "fail" +fi +rm -f test_session tp_localhost xqc_token + +clear_log +echo -e "send 1M data on multiple paths with multipath vertion 05" +sudo ./test_client -s 1024000 -l d -t 1 -M -i lo -i lo -E -v 5 > stdlog +cli_result=`grep "multipath version negotiation succeed on multipath 05" clog` +if [ -n "$cli_result" ]; then + echo ">>>>>>>> pass:1" + case_print_result "MPNS_send_data_with_multipath_05" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "MPNS_send_data_with_multipath_05" "fail" +fi +rm -f test_session tp_localhost xqc_token + killall test_server ./test_server -l d -e -M -R 1 > /dev/null & sleep 1 @@ -4097,6 +4129,49 @@ else case_print_result "freeze_path1" "fail" fi +killall test_server +stdbuf -oL ./test_server -l d -e -M > /dev/null & +sleep 1 + +rm -rf tp_localhost test_session xqc_token +clear_log +echo -e "probing standby paths ...\c" +sudo ./test_client -s 1024000 -l d -E -e 1 --epoch_timeout 2000000 -t 4 -M -i lo -i lo -x 501 -y > stdlog +clog_res1=`grep -E "|xqc_path_standby_probe|PING|path:1|" clog` +if [ -n "$clog_res1" ] ; then + echo ">>>>>>>> pass:1" + case_print_result "probing_standby_path" "pass" +else + echo ">>>>>>>> pass:0" + case_print_result "probing_standby_path" "fail" +fi + + +sudo rm -rf tp_localhost test_session xqc_token clog stdlog ckeys.log +clear_log +echo -e "conn_rate_throttling ...\c" +result=`./test_client -s 1024000 -l d -E --rate_limit 1000000 |grep ">>>>>>>> pass"` +errlog=`grep_err_log` +echo "$result" +if [ -z "$errlog" ] && [ "$result" == ">>>>>>>> pass:1" ]; then + case_print_result "conn_rate_throttling" "pass" +else + case_print_result "conn_rate_throttling" "fail" + echo "$errlog" +fi + +clear_log +echo -e "stream_rate_throttling ...\c" +result=`./test_client -s 1024000 -l d -E -x 109 |grep ">>>>>>>> pass"` +errlog=`grep_err_log` +echo "$result" +if [ -z "$errlog" ] && [ "$result" == ">>>>>>>> pass:1" ]; then + case_print_result "stream_rate_throttling" "pass" +else + case_print_result "stream_rate_throttling" "fail" + echo "$errlog" +fi + killall test_server cd - diff --git a/scripts/xquic.lds b/scripts/xquic.lds index 6524af00f..17cdd7b22 100644 --- a/scripts/xquic.lds +++ b/scripts/xquic.lds @@ -95,7 +95,6 @@ XQUIC_VERS_1.0 { xqc_datagram_send_multiple; xqc_datagram_set_user_data; xqc_datagram_get_user_data; - xqc_path_get_stats; xqc_h3_ext_datagram_get_mss; xqc_h3_ext_datagram_set_user_data; xqc_h3_ext_datagram_get_user_data; @@ -116,6 +115,8 @@ XQUIC_VERS_1.0 { xqc_conn_get_conn_settings_template; xqc_conn_get_lastest_rtt; xqc_log_enable; + xqc_h3_request_update_settings; + xqc_stream_update_settings; local: *; }; diff --git a/src/http3/xqc_h3_conn.c b/src/http3/xqc_h3_conn.c index 79f201855..70400c382 100644 --- a/src/http3/xqc_h3_conn.c +++ b/src/http3/xqc_h3_conn.c @@ -483,7 +483,7 @@ xqc_h3_conn_create_uni_stream(xqc_h3_conn_t *h3c, xqc_h3_stream_type_t h3s_type) /* create transport stream */ xqc_stream_t *stream = xqc_create_stream_with_conn( - h3c->conn, XQC_UNDEFINE_STREAM_ID, stream_type, NULL); + h3c->conn, XQC_UNDEFINE_STREAM_ID, stream_type, NULL, NULL); if (!stream) { xqc_log(h3c->log, XQC_LOG_ERROR, "|xqc_create_stream_with_conn error|type:%d|", h3s_type); goto error; diff --git a/src/http3/xqc_h3_ext_bytestream.c b/src/http3/xqc_h3_ext_bytestream.c index ebd99f947..070d46acf 100644 --- a/src/http3/xqc_h3_ext_bytestream.c +++ b/src/http3/xqc_h3_ext_bytestream.c @@ -208,7 +208,7 @@ xqc_h3_ext_bytestream_create(xqc_engine_t *engine, xqc_h3_conn_t *h3_conn; int ret; - stream = xqc_stream_create(engine, cid, NULL); + stream = xqc_stream_create(engine, cid, NULL, NULL); if (!stream) { xqc_log(engine->log, XQC_LOG_ERROR, "|xqc_stream_create error|"); return NULL; diff --git a/src/http3/xqc_h3_request.c b/src/http3/xqc_h3_request.c index b53fd5a2c..7c6e74e4f 100644 --- a/src/http3/xqc_h3_request.c +++ b/src/http3/xqc_h3_request.c @@ -11,14 +11,15 @@ xqc_h3_request_t * -xqc_h3_request_create(xqc_engine_t *engine, const xqc_cid_t *cid, void *user_data) +xqc_h3_request_create(xqc_engine_t *engine, + const xqc_cid_t *cid, xqc_stream_settings_t *settings, void *user_data) { xqc_stream_t *stream; xqc_h3_stream_t *h3_stream; xqc_h3_request_t *h3_request; xqc_h3_conn_t *h3_conn; - stream = xqc_stream_create(engine, cid, NULL); + stream = xqc_stream_create(engine, cid, settings, NULL); if (!stream) { xqc_log(engine->log, XQC_LOG_ERROR, "|xqc_stream_create error|"); return NULL; @@ -51,8 +52,6 @@ xqc_h3_request_destroy(xqc_h3_request_t *h3_request) /* print request statistic log */ xqc_request_stats_t stats = xqc_h3_request_get_stats(h3_request); - char path_info_buff[100 * XQC_MAX_PATHS_COUNT] = {'\0'}; - xqc_h3s_path_metrics_print(h3s, path_info_buff, 50 * XQC_MAX_PATHS_COUNT); xqc_usec_t create_time = h3_request->h3r_begin_time; @@ -60,7 +59,7 @@ xqc_h3_request_destroy(xqc_h3_request_t *h3_request) "|rcvd_bdy_sz:%uz|snd_bdy_sz:%uz|rcvd_hdr_sz:%uz|snd_hdr_sz:%uz" "|create:%ui|blkd:%ui|nblkd:%ui|hdr_b:%ui|hdr_e:%ui|bdy_b:%ui|fin:%ui|recv_end:%ui" "|hrd_send:%ui|bdy_send:%ui|fin_send:%ui|fin_ack:%ui|last_send:%ui|last_recv:%ui" - "|mp_state:%d|path_info:%s|", + "|mp_state:%d|path_info:%s|comp_hdr_s:%uz|comp_hdr_r:%uz|", h3s->stream_id, stats.stream_close_msg ? : "", stats.stream_err, stats.recv_body_size, stats.send_body_size, stats.recv_header_size, stats.send_header_size, @@ -78,7 +77,8 @@ xqc_h3_request_destroy(xqc_h3_request_t *h3_request) xqc_calc_delay(stats.stream_fin_ack_time, create_time), xqc_calc_delay(h3_request->h3_stream->h3c->conn->conn_last_send_time, create_time), xqc_calc_delay(h3_request->h3_stream->h3c->conn->conn_last_recv_time, create_time), - stats.mp_state, path_info_buff); + stats.mp_state, stats.stream_info, stats.send_hdr_compressed, + stats.recv_hdr_compressed); if (h3_request->request_if->h3_request_close_notify) { h3_request->request_if->h3_request_close_notify(h3_request, h3_request->user_data); @@ -171,34 +171,104 @@ xqc_h3_request_create_inner(xqc_h3_conn_t *h3_conn, xqc_h3_stream_t *h3_stream, void xqc_stream_info_print(xqc_h3_stream_t *h3_stream, xqc_request_stats_t *stats) { + xqc_h3_conn_t *h3c = h3_stream->h3c; char *buff = stats->stream_info; size_t buff_size = XQC_STREAM_INFO_LEN; - size_t cursor = 0, ret = 0; + int i; + int flag = 0; + uint32_t mp_settings = 0; + + if (h3c->conn->handshake_complete_time > 0) { + flag = 1; + } + + if (h3c->conn->conn_settings.enable_stream_rate_limit) { + flag |= 1 << 1; + } + + if (h3c->conn->enable_multipath) { + mp_settings |= 1; + } + + if (h3c->conn->local_settings.enable_multipath) { + mp_settings |= (1 << 1); + } + + if (h3c->conn->remote_settings.enable_multipath) { + mp_settings |= (1 << 2); + } + + if (h3c->conn->conn_settings.multipath_version == XQC_MULTIPATH_05) { + mp_settings |= (1 << 3); + } + + if (h3c->conn->local_settings.multipath_version == XQC_MULTIPATH_05) { + mp_settings |= (1 << 4); + } + + if (h3c->conn->remote_settings.multipath_version == XQC_MULTIPATH_05) { + mp_settings |= (1 << 5); + } + + ret = snprintf(buff, buff_size, "(%d,%"PRIu64",%d)#", + flag, h3_stream->recv_rate_limit, mp_settings); + + cursor += ret; + + if (cursor >= buff_size) { + goto full; + } for (int i = 0; i < XQC_MAX_PATHS_COUNT; ++i) { if ((h3_stream->paths_info[i].path_send_bytes > 0) || (h3_stream->paths_info[i].path_recv_bytes > 0)) { - /* check buffer size */ - if (cursor + 100 >= buff_size) { - break; - } - ret = snprintf(buff + cursor, buff_size - cursor, - "#%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64, + "%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64"-%d#", h3_stream->paths_info[i].path_id, h3_stream->paths_info[i].path_pkt_send_count, h3_stream->paths_info[i].path_pkt_recv_count, h3_stream->paths_info[i].path_send_bytes, - h3_stream->paths_info[i].path_send_reinject_bytes, h3_stream->paths_info[i].path_recv_bytes, - h3_stream->paths_info[i].path_recv_reinject_bytes, - h3_stream->paths_info[i].path_recv_effective_bytes, - h3_stream->paths_info[i].path_recv_effective_reinject_bytes); + h3_stream->paths_info[i].path_srtt, + h3_stream->paths_info[i].path_app_status); cursor += ret; + + if (cursor >= buff_size) { + goto full; + } } } + +full: + cursor = xqc_min(cursor, buff_size); + for (i = cursor - 1; i >= 0; i--) { + if (buff[i] == '-' || buff[i] == '#') { + buff[i] = '\0'; + break; + } + } + buff[buff_size - 1] = '\0'; +} + + +xqc_int_t +xqc_h3_request_update_settings(xqc_h3_request_t *h3_request, + xqc_stream_settings_t *settings) +{ + if (h3_request && settings + && h3_request->h3_stream && h3_request->h3_stream->stream) + { + if (xqc_stream_update_settings(h3_request->h3_stream->stream, + settings) == XQC_OK) + { + h3_request->h3_stream->recv_rate_limit = settings->recv_rate_bytes_per_sec; + return XQC_OK; + } + } + + return -XQC_EPARAM; } xqc_request_stats_t @@ -226,6 +296,9 @@ xqc_h3_request_get_stats(xqc_h3_request_t *h3_request) stats.stream_fin_send_time = h3_request->stream_fin_send_time; stats.stream_fin_ack_time = h3_request->stream_fin_ack_time; stats.stream_close_msg = h3_request->stream_close_msg; + stats.send_hdr_compressed = h3_request->compressed_header_sent; + stats.recv_hdr_compressed = h3_request->compressed_header_recvd; + stats.rate_limit = h3_request->h3_stream->recv_rate_limit; xqc_h3_stream_get_path_info(h3_request->h3_stream); xqc_request_path_metrics_print(h3_request->h3_stream->h3c->conn, diff --git a/src/http3/xqc_h3_request.h b/src/http3/xqc_h3_request.h index 4e75f50ab..c76c18ca1 100644 --- a/src/http3/xqc_h3_request.h +++ b/src/http3/xqc_h3_request.h @@ -27,6 +27,7 @@ typedef struct xqc_h3_request_s { xqc_request_notify_flag_t read_flag; /* compressed header size recved */ + size_t compressed_header_recvd; size_t header_recvd; /* received header buf */ xqc_http_headers_t h3_header[XQC_H3_REQUEST_MAX_HEADERS_CNT]; @@ -40,6 +41,7 @@ typedef struct xqc_h3_request_s { size_t body_recvd_final_size; /* compressed header size sent */ + size_t compressed_header_sent; size_t header_sent; /* send body statistic information */ @@ -63,10 +65,6 @@ typedef struct xqc_h3_request_s { } xqc_h3_request_t; - -xqc_h3_request_t *xqc_h3_request_create(xqc_engine_t *engine, const xqc_cid_t *cid, - void *user_data); - xqc_h3_request_t *xqc_h3_request_create_inner(xqc_h3_conn_t *h3_conn, xqc_h3_stream_t *h3_stream, void *user_data); diff --git a/src/http3/xqc_h3_stream.c b/src/http3/xqc_h3_stream.c index bca47bb83..9c960b062 100644 --- a/src/http3/xqc_h3_stream.c +++ b/src/http3/xqc_h3_stream.c @@ -36,6 +36,7 @@ xqc_h3_stream_create(xqc_h3_conn_t *h3c, xqc_stream_t *stream, xqc_h3_stream_typ xqc_init_list_head(&h3s->blocked_buf); h3s->ctx = xqc_qpack_create_req_ctx(stream->stream_id); h3s->log = h3c->log; + h3s->recv_rate_limit = stream->recv_rate_bytes_per_sec; stream->user_data = h3s; stream->stream_flag |= XQC_STREAM_FLAG_HAS_H3; @@ -379,8 +380,10 @@ xqc_h3_stream_send_headers(xqc_h3_stream_t *h3s, xqc_http_headers_t *headers, ui xqc_log(h3c->log, XQC_LOG_ERROR, "|xqc_h3_stream_write_headers error|ret:%z||stream_id:%ui", write, h3s->stream_id); XQC_H3_CONN_ERR(h3c, H3_INTERNAL_ERROR, write); - } + } else { + h3s->h3r->compressed_header_sent += write; + } /* header_sent is the sum of plaintext header name value length */ h3s->h3r->header_sent += headers->total_len; @@ -964,6 +967,7 @@ xqc_h3_stream_process_request(xqc_h3_stream_t *h3s, unsigned char *data, size_t if (pctx->frame.len == pctx->frame.consumed_len) { xqc_log_event(h3s->log, HTTP_FRAME_PARSED, h3s); fin = 1; + h3s->h3r->compressed_header_recvd += pctx->frame.len; xqc_h3_frm_reset_pctx(pctx); xqc_qpack_clear_req_ctx(h3s->ctx); if (fin_flag && processed == data_len) { diff --git a/src/http3/xqc_h3_stream.h b/src/http3/xqc_h3_stream.h index 5867974fa..b1a151149 100644 --- a/src/http3/xqc_h3_stream.h +++ b/src/http3/xqc_h3_stream.h @@ -147,6 +147,8 @@ typedef struct xqc_h3_stream_s { /* referred count of h3 stream */ uint32_t ref_cnt; + uint64_t recv_rate_limit; + } xqc_h3_stream_t; diff --git a/src/transport/reinjection_control/xqc_reinj_deadline.c b/src/transport/reinjection_control/xqc_reinj_deadline.c index dc1e9e841..60d29a716 100644 --- a/src/transport/reinjection_control/xqc_reinj_deadline.c +++ b/src/transport/reinjection_control/xqc_reinj_deadline.c @@ -48,7 +48,7 @@ xqc_deadline_reinj_can_reinject_before_sched(xqc_deadline_reinj_ctl_t *rctl, { xqc_connection_t *conn = rctl->conn; xqc_usec_t now = xqc_monotonic_timestamp(); - xqc_usec_t min_srtt = xqc_conn_get_min_srtt(conn); + xqc_usec_t min_srtt = xqc_conn_get_min_srtt(conn, 0); double factor = conn->conn_settings.reinj_flexible_deadline_srtt_factor; double flexible = factor * min_srtt; @@ -56,10 +56,11 @@ xqc_deadline_reinj_can_reinject_before_sched(xqc_deadline_reinj_ctl_t *rctl, uint64_t lower_bound = conn->conn_settings.reinj_deadline_lower_bound; double deadline = xqc_max(xqc_min(flexible, (double)hard), (double)lower_bound); - xqc_log(conn->log, XQC_LOG_DEBUG, "|deadline:%f|factor:%.4f|min_srtt:%ui|flexible:%f|hard:%ui|lower_bound:%ui|", - deadline, factor, min_srtt, flexible, hard, lower_bound); + xqc_log(conn->log, XQC_LOG_DEBUG, "|deadline:%f|factor:%.4f|min_srtt:%ui|flexible:%f|hard:%ui|lower_bound:%ui|now:%ui|sent_time:%ui|frame:%s|", + deadline, factor, min_srtt, flexible, hard, lower_bound, now, po->po_sent_time, xqc_frame_type_2_str(po->po_frame_types)); - if ((po->po_frame_types & XQC_FRAME_BIT_STREAM) + if (((po->po_frame_types & XQC_FRAME_BIT_STREAM) + || (po->po_frame_types & XQC_FRAME_BIT_MAX_STREAM_DATA)) && !(po->po_flag & XQC_POF_NOT_REINJECT) && !(XQC_MP_PKT_REINJECTED(po)) && (po->po_flag & XQC_POF_IN_FLIGHT) diff --git a/src/transport/reinjection_control/xqc_reinj_default.c b/src/transport/reinjection_control/xqc_reinj_default.c index 5a1c9c8f6..265ada8b1 100644 --- a/src/transport/reinjection_control/xqc_reinj_default.c +++ b/src/transport/reinjection_control/xqc_reinj_default.c @@ -49,7 +49,8 @@ xqc_default_reinj_can_reinject_after_sched(xqc_default_reinj_ctl_t *rctl, xqc_connection_t *conn = rctl->conn; if (xqc_list_empty(&conn->conn_send_queue->sndq_send_packets) - && (po->po_frame_types & XQC_FRAME_BIT_STREAM) + && ((po->po_frame_types & XQC_FRAME_BIT_STREAM) + || (po->po_frame_types & XQC_FRAME_BIT_MAX_STREAM_DATA)) && !(po->po_flag & XQC_POF_NOT_REINJECT) && !(XQC_MP_PKT_REINJECTED(po)) && (po->po_flag & XQC_POF_IN_FLIGHT)) diff --git a/src/transport/scheduler/xqc_scheduler_backup.c b/src/transport/scheduler/xqc_scheduler_backup.c index 1c355df58..a6950779e 100644 --- a/src/transport/scheduler/xqc_scheduler_backup.c +++ b/src/transport/scheduler/xqc_scheduler_backup.c @@ -7,9 +7,6 @@ #include "src/transport/scheduler/xqc_scheduler_common.h" #include "src/transport/xqc_send_ctl.h" -#define BACKUP_PATH_PROBE_SUCCESS_TIMEOUT 3000000 -#define BACKUP_PATH_PROBE_TIMEOUT 3000000 - static size_t xqc_backup_scheduler_size() @@ -23,48 +20,6 @@ xqc_backup_scheduler_init(void *scheduler, xqc_log_t *log, xqc_scheduler_params_ return; } -static inline xqc_bool_t -xqc_path_can_schedule(xqc_path_ctx_t *path, xqc_packet_out_t *packet_out) -{ - if (path->path_state != XQC_PATH_STATE_ACTIVE) { - return XQC_FALSE; - } - - if (packet_out->po_flag & XQC_POF_NOT_SCHEDULE) { - if ((path->tra_path_status != XQC_TRA_PATH_STATUS_IN_USE) && (path->parent_conn->in_use_active_path_count > 0)) { - return XQC_FALSE; - } - } - - return XQC_TRUE; -} - -static inline xqc_bool_t -xqc_path_can_reinject(xqc_path_ctx_t *path, xqc_packet_out_t *packet_out) -{ - if (path->path_state != XQC_PATH_STATE_ACTIVE) { - return XQC_FALSE; - } - - if (path->path_id == packet_out->po_path_id) { - return XQC_FALSE; - } - - if (packet_out->po_flag & XQC_POF_NOT_REINJECT) { - if ((path->tra_path_status != XQC_TRA_PATH_STATUS_IN_USE) && (path->app_path_status != XQC_APP_PATH_STATUS_AVAILABLE)) { - return XQC_FALSE; - } - } - - return XQC_TRUE; -} - -static inline xqc_bool_t -xqc_path_can_probe(xqc_path_ctx_t *path) -{ - return (path->path_state == XQC_PATH_STATE_ACTIVE) - && (path->tra_path_status == XQC_TRA_PATH_STATUS_BACKUP); -} xqc_path_ctx_t * xqc_backup_scheduler_get_path(void *scheduler, @@ -72,6 +27,7 @@ xqc_backup_scheduler_get_path(void *scheduler, xqc_bool_t *cc_blocked) { xqc_path_ctx_t *best_path = NULL; + xqc_path_ctx_t *best_standby_path = NULL; xqc_list_head_t *pos, *next; xqc_path_ctx_t *path; @@ -79,7 +35,9 @@ xqc_backup_scheduler_get_path(void *scheduler, /* min RTT */ uint64_t min_rtt = XQC_MAX_UINT64_VALUE; + uint64_t min_rtt_standby = XQC_MAX_UINT64_VALUE; uint64_t path_srtt; + uint32_t avail_path_cnt = 0; xqc_bool_t reached_cwnd_check = XQC_FALSE; if (cc_blocked) { @@ -89,15 +47,22 @@ xqc_backup_scheduler_get_path(void *scheduler, xqc_list_for_each_safe(pos, next, &conn->conn_paths_list) { path = xqc_list_entry(pos, xqc_path_ctx_t, path_list); - if (reinject) { - if (!xqc_path_can_reinject(path, packet_out)) { - continue; - } + /* skip inactive paths */ + if (path->path_state != XQC_PATH_STATE_ACTIVE) { + continue; + } - } else { - if (!xqc_path_can_schedule(path, packet_out)) { - continue; - } + /* skip frozen paths */ + if (path->app_path_status == XQC_APP_PATH_STATUS_FROZEN) { + continue; + } + + if (path->app_path_status == XQC_APP_PATH_STATUS_AVAILABLE) { + avail_path_cnt++; + } + + if (reinject && (packet_out->po_path_id == path->path_id)) { + continue; } if (!reached_cwnd_check) { @@ -120,195 +85,81 @@ xqc_backup_scheduler_get_path(void *scheduler, conn, path->path_id, path_srtt); if (path_srtt < min_rtt) { - best_path = path; - min_rtt = path_srtt; + if (path->app_path_status == XQC_APP_PATH_STATUS_AVAILABLE) { + best_path = path; + min_rtt = path_srtt; + + } else { + best_standby_path = path; + min_rtt_standby = path_srtt; + } } } - if (best_path == NULL) { - xqc_log(conn->log, XQC_LOG_DEBUG, "|No available paths to schedule|conn:%p|", conn); - - } else { - xqc_log(conn->log, XQC_LOG_DEBUG, "|best path:%ui|frame_type:%s|", - best_path->path_id, xqc_frame_type_2_str(packet_out->po_frame_types)); - } - - return best_path; - -} - -static inline xqc_int_t -xqc_backup_probe_standby_path(xqc_connection_t *conn, - xqc_path_ctx_t **default_path, xqc_path_ctx_t **standby_path) -{ - xqc_int_t ret; - xqc_list_head_t *pos, *next; - xqc_path_ctx_t *path; - xqc_send_ctl_t *send_ctl; - xqc_usec_t now = xqc_monotonic_timestamp(); - xqc_usec_t last_time; - - 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 (path->app_path_status == XQC_APP_PATH_STATUS_AVAILABLE) { - *default_path = path; - - } else if (path->app_path_status == XQC_APP_PATH_STATUS_STANDBY) { - *standby_path = path; - } - - if (conn->conn_settings.standby_path_probe_timeout > 0 - && xqc_path_can_probe(path)) + if (best_path == NULL) { + if (best_standby_path != NULL + && (avail_path_cnt == 0 || reinject)) { - send_ctl = path->path_send_ctl; - last_time = send_ctl->ctl_time_of_last_sent_ack_eliciting_packet[XQC_PNS_APP_DATA]; - - if ((now - last_time) >= conn->conn_settings.standby_path_probe_timeout * 1000) { - ret = xqc_path_standby_probe(path); - if (ret != XQC_OK) { - xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_path_standby_probe error|"); - return ret; - } - } + best_path = best_standby_path; + } else { + xqc_log(conn->log, XQC_LOG_DEBUG, "|No available paths to schedule|conn:%p|", conn); } } - return XQC_OK; -} - -static inline xqc_bool_t -xqc_default_path_is_in_use(xqc_path_ctx_t *default_path, xqc_path_ctx_t *standby_path) -{ - return (default_path->tra_path_status == XQC_TRA_PATH_STATUS_IN_USE) - && (standby_path->tra_path_status == XQC_TRA_PATH_STATUS_BACKUP); -} - -static inline xqc_bool_t -xqc_standby_path_is_in_use(xqc_path_ctx_t *default_path, xqc_path_ctx_t *standby_path) -{ - return (default_path->tra_path_status == XQC_TRA_PATH_STATUS_BACKUP) - && (standby_path->tra_path_status == XQC_TRA_PATH_STATUS_IN_USE); -} - -static inline xqc_bool_t -xqc_default_path_need_degrade(xqc_connection_t *conn, - xqc_path_ctx_t *default_path, xqc_path_ctx_t *standby_path) -{ - if (conn->transport_cbs.path_status_controller) { - return conn->transport_cbs.path_status_controller(conn, &conn->scid_set.user_scid, - default_path->path_id, XQC_PATH_DEGRADE, - xqc_conn_get_user_data(conn)); - } - - xqc_send_ctl_t *send_ctl = default_path->path_send_ctl; - if (send_ctl->ctl_pto_count_since_last_tra_path_status_changed > conn->conn_settings.path_unreachable_pto_count) { - return XQC_TRUE; - } - - return XQC_FALSE; -} - -static inline xqc_bool_t -xqc_default_path_need_recovery(xqc_connection_t *conn, - xqc_path_ctx_t *default_path, xqc_path_ctx_t *standby_path) -{ - if (conn->transport_cbs.path_status_controller) { - return conn->transport_cbs.path_status_controller(conn, &conn->scid_set.user_scid, - default_path->path_id, XQC_PATH_RECOVERY, - xqc_conn_get_user_data(conn)); - } - xqc_send_ctl_t *send_ctl = default_path->path_send_ctl; - if (send_ctl->ctl_send_count - send_ctl->ctl_send_count_at_last_tra_path_status_changed_time > 3 - && xqc_monotonic_timestamp() - default_path->last_tra_path_status_changed_time > BACKUP_PATH_PROBE_TIMEOUT - && send_ctl->ctl_pto_count_since_last_tra_path_status_changed < 1) - { - return XQC_TRUE; + if (best_path) { + xqc_log(conn->log, XQC_LOG_DEBUG, "|best path:%ui|frame_type:%s|app_status:%d|", + best_path->path_id, xqc_frame_type_2_str(packet_out->po_frame_types), + best_path->app_path_status); } - return XQC_FALSE; -} - -void -xqc_probe_before_use(xqc_connection_t *conn, - xqc_path_ctx_t *next_in_use_path, xqc_path_ctx_t *next_backup_path) -{ - xqc_usec_t now = xqc_monotonic_timestamp(); - xqc_usec_t last_recv_time = next_in_use_path->path_send_ctl->ctl_largest_recv_time[XQC_PNS_APP_DATA]; - xqc_usec_t last_send_time = next_in_use_path->path_send_ctl->ctl_time_of_last_sent_ack_eliciting_packet[XQC_PNS_APP_DATA]; - - if ((now - last_recv_time) <= BACKUP_PATH_PROBE_SUCCESS_TIMEOUT) { - xqc_set_transport_path_status(next_in_use_path, XQC_TRA_PATH_STATUS_IN_USE, now); - xqc_set_transport_path_status(next_backup_path, XQC_TRA_PATH_STATUS_BACKUP, now); - - } else if ((now - last_send_time) >= BACKUP_PATH_PROBE_TIMEOUT) { - xqc_int_t ret = xqc_path_standby_probe(next_in_use_path); - if (ret != XQC_OK) { - xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_path_standby_probe error|"); - return; - } - } + return best_path; } - -void +void xqc_backup_scheduler_handle_conn_event(void *scheduler, xqc_connection_t *conn, xqc_scheduler_conn_event_t event, void *event_arg) { - if (event == XQC_SCHED_EVENT_CONN_ROUND_START) { - - if (conn->active_path_count < 2) { - return; - } - - if (XQC_UNLIKELY(conn->conn_state >= XQC_CONN_STATE_CLOSING)) { - return; - } + xqc_list_head_t *pos, *next; + xqc_path_ctx_t *path; + xqc_send_ctl_t *send_ctl; + xqc_usec_t now = 0, deadline; - xqc_int_t ret = XQC_ERROR; + if (event == XQC_SCHED_EVENT_CONN_ROUND_START + && conn->conn_settings.standby_path_probe_timeout + && conn->enable_multipath + && (conn->conn_state == XQC_CONN_STATE_ESTABED)) + { + /* check if we need to probe standby 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 + && path->app_path_status == XQC_APP_PATH_STATUS_STANDBY) + { + if (!now) { + now = xqc_monotonic_timestamp(); + } - xqc_path_ctx_t *default_path = NULL; - xqc_path_ctx_t *standby_path = NULL; + send_ctl = path->path_send_ctl; - ret = xqc_backup_probe_standby_path(conn, &default_path, &standby_path); - if (ret != XQC_OK) { - xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_backup_probe_standby_path error|ret:%d|", ret); - return; - } + deadline = send_ctl->ctl_time_of_last_sent_ack_eliciting_packet[XQC_PNS_APP_DATA] + conn->conn_settings.standby_path_probe_timeout * 1000; - if (default_path == NULL || standby_path == NULL) { - xqc_log(conn->log, XQC_LOG_DEBUG, "|identify default/standby path error|"); - return; - } + xqc_log(conn->log, XQC_LOG_DEBUG, "|standby_probe|path:%ui|deadline:%ui|now:%ui|now>=deadline:%d|last_send:%ui|", + path->path_id, deadline, now, now >= deadline, send_ctl->ctl_time_of_last_sent_ack_eliciting_packet[XQC_PNS_APP_DATA]); - if (xqc_default_path_is_in_use(default_path, standby_path)) { - if (xqc_default_path_need_degrade(conn, default_path, standby_path)) { - xqc_probe_before_use(conn, standby_path, default_path); - } - - } else if (xqc_standby_path_is_in_use(default_path, standby_path)) { - if (xqc_default_path_need_recovery(conn, default_path, standby_path)) { - xqc_probe_before_use(conn,default_path, standby_path); + if (now >= deadline) { + xqc_path_standby_probe(path); + } } - - } else { - xqc_log(conn->log, XQC_LOG_ERROR, "|path status error|"); - return; } } - } const xqc_scheduler_callback_t xqc_backup_scheduler_cb = { - .xqc_scheduler_size = xqc_backup_scheduler_size, - .xqc_scheduler_init = xqc_backup_scheduler_init, - .xqc_scheduler_get_path = xqc_backup_scheduler_get_path, + .xqc_scheduler_size = xqc_backup_scheduler_size, + .xqc_scheduler_init = xqc_backup_scheduler_init, + .xqc_scheduler_get_path = xqc_backup_scheduler_get_path, .xqc_scheduler_handle_conn_event = xqc_backup_scheduler_handle_conn_event, }; \ No newline at end of file diff --git a/src/transport/xqc_conn.c b/src/transport/xqc_conn.c index 8e05900aa..d98ee8f73 100644 --- a/src/transport/xqc_conn.c +++ b/src/transport/xqc_conn.c @@ -29,6 +29,7 @@ #include "src/transport/xqc_reinjection.h" #include "src/tls/xqc_tls.h" #include "src/transport/xqc_datagram.h" +#include xqc_conn_settings_t default_conn_settings = { @@ -41,6 +42,7 @@ xqc_conn_settings_t default_conn_settings = { .init_idle_time_out = XQC_CONN_INITIAL_IDLE_TIMEOUT, .idle_time_out = XQC_CONN_DEFAULT_IDLE_TIMEOUT, .enable_multipath = 0, + .multipath_version = XQC_MULTIPATH_04, .spurious_loss_detect_on = 0, .anti_amplification_limit = XQC_DEFAULT_ANTI_AMPLIFICATION_LIMIT, .keyupdate_pkt_threshold = 0, @@ -57,15 +59,17 @@ xqc_conn_settings_t default_conn_settings = { .datagram_force_retrans_on = 0, .datagram_redundant_probe = 0, - .reinj_flexible_deadline_srtt_factor = 2.0, - .reinj_hard_deadline = XQC_MAX_UINT64_VALUE, - .reinj_deadline_lower_bound = 0, + .reinj_flexible_deadline_srtt_factor = 1.1, + .reinj_hard_deadline = 500000, /* 500ms */ + .reinj_deadline_lower_bound = 20000, /* 20ms */ - .path_unreachable_pto_count = 6, .standby_path_probe_timeout = 0, .enable_pmtud = 0, .pmtud_probing_interval = 500000, .marking_reinjection = 0, + + .recv_rate_bytes_per_sec = 0, + .enable_stream_rate_limit = 0, }; @@ -101,6 +105,13 @@ xqc_server_set_conn_settings(const xqc_conn_settings_t *settings) default_conn_settings.marking_reinjection = settings->marking_reinjection; default_conn_settings.mp_ack_on_any_path = settings->mp_ack_on_any_path; default_conn_settings.mp_ping_on = settings->mp_ping_on; + default_conn_settings.recv_rate_bytes_per_sec = settings->recv_rate_bytes_per_sec; + default_conn_settings.enable_stream_rate_limit = settings->enable_stream_rate_limit; + default_conn_settings.init_recv_window = settings->init_recv_window; + + if (default_conn_settings.init_recv_window) { + default_conn_settings.init_recv_window = xqc_max(default_conn_settings.init_recv_window, XQC_QUIC_MAX_MSS); + } if (settings->pmtud_probing_interval) { default_conn_settings.pmtud_probing_interval = settings->pmtud_probing_interval; @@ -148,6 +159,13 @@ xqc_server_set_conn_settings(const xqc_conn_settings_t *settings) default_conn_settings.enable_multipath = settings->enable_multipath; + if (xqc_conn_is_current_mp_version_supported(settings->multipath_version) == XQC_OK) { + default_conn_settings.multipath_version = settings->multipath_version; + + } else { + default_conn_settings.multipath_version = XQC_MULTIPATH_04; + } + default_conn_settings.scheduler_callback = settings->scheduler_callback; default_conn_settings.reinj_ctl_callback = settings->reinj_ctl_callback; default_conn_settings.mp_enable_reinjection = settings->mp_enable_reinjection; @@ -176,12 +194,9 @@ xqc_server_set_conn_settings(const xqc_conn_settings_t *settings) default_conn_settings.reinj_deadline_lower_bound = settings->reinj_deadline_lower_bound; } - if (settings->path_unreachable_pto_count > 0) { - default_conn_settings.path_unreachable_pto_count = settings->path_unreachable_pto_count; - } - if (settings->standby_path_probe_timeout > 0) { - default_conn_settings.standby_path_probe_timeout = settings->standby_path_probe_timeout; + /* no less than 500ms */ + default_conn_settings.standby_path_probe_timeout = xqc_max(settings->standby_path_probe_timeout, XQC_MIN_STANDBY_RPOBE_TIMEOUT); } } @@ -215,7 +230,7 @@ static const char * const xqc_conn_flag_to_str[XQC_CONN_FLAG_SHIFT_NUM] = { [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_NEW_CID_RECEIVED_SHIFT] = "NEW_CID_RECEIVED", + [XQC_CONN_FLAG_NEW_CID_ACKED_SHIFT] = "NEW_CID_ACKED", [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", @@ -228,6 +243,9 @@ static const char * const xqc_conn_flag_to_str[XQC_CONN_FLAG_SHIFT_NUM] = { [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_SCID_SHIFT] = "MP_WAIT_SCID", + [XQC_CONN_FLAG_MP_WAIT_DCID_SHIFT] = "MP_WAIT_DCID", + [XQC_CONN_FLAG_MP_READY_NOTIFY_SHIFT] = "MP_READY", }; unsigned char g_conn_flag_buf[1024]; @@ -305,15 +323,28 @@ xqc_conn_init_trans_settings(xqc_connection_t *conn) /* set local default setting values */ ls->max_streams_bidi = 1024; - ls->max_stream_data_bidi_remote = 16 * 1024 * 1024; - ls->max_stream_data_bidi_local = 16 * 1024 * 1024; + ls->max_stream_data_bidi_remote = 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; + } ls->max_streams_uni = 1024; - ls->max_stream_data_uni = 16 * 1024 * 1024; + ls->max_stream_data_uni = XQC_MAX_RECV_WINDOW; - /* 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; + 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; @@ -322,6 +353,8 @@ xqc_conn_init_trans_settings(xqc_connection_t *conn) 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->max_datagram_frame_size = conn->conn_settings.max_datagram_frame_size; ls->disable_active_migration = ls->enable_multipath ? 0 : 1; @@ -433,6 +466,17 @@ xqc_conn_create(xqc_engine_t *engine, xqc_cid_t *dcid, xqc_cid_t *scid, 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 < default_conn_settings.max_pkt_out_size) { xc->conn_settings.max_pkt_out_size = default_conn_settings.max_pkt_out_size; } @@ -479,10 +523,6 @@ xqc_conn_create(xqc_engine_t *engine, xqc_cid_t *dcid, xqc_cid_t *scid, xc->conn_settings.anti_amplification_limit = XQC_DEFAULT_ANTI_AMPLIFICATION_LIMIT; } - if (xc->conn_settings.path_unreachable_pto_count == 0) { - xc->conn_settings.path_unreachable_pto_count = default_conn_settings.path_unreachable_pto_count; - } - if (xc->conn_settings.reinj_flexible_deadline_srtt_factor == 0) { xc->conn_settings.reinj_flexible_deadline_srtt_factor = default_conn_settings.reinj_flexible_deadline_srtt_factor; } @@ -491,6 +531,10 @@ xqc_conn_create(xqc_engine_t *engine, xqc_cid_t *dcid, xqc_cid_t *scid, xc->conn_settings.reinj_hard_deadline = 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_04; + } + xqc_conn_init_trans_settings(xc); xqc_conn_init_flow_ctl(xc); xqc_conn_init_key_update_ctx(xc); @@ -1245,6 +1289,7 @@ xqc_send_burst(xqc_connection_t *conn, xqc_path_ctx_t *path, struct iovec *iov, if (sent < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|error send mmsg|"); 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, "|socket exception, close connection|"); conn->conn_state = XQC_CONN_STATE_CLOSED; @@ -1339,6 +1384,22 @@ xqc_conn_schedule_packets(xqc_connection_t *conn, xqc_list_head_t *head, } } +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) { @@ -1356,6 +1417,9 @@ xqc_on_packets_send_burst(xqc_connection_t *conn, xqc_path_ctx_t *path, ssize_t } 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 @@ -1774,6 +1838,7 @@ xqc_send(xqc_connection_t *conn, xqc_path_ctx_t *path, unsigned char *data, unsi /* 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; @@ -1815,6 +1880,7 @@ xqc_process_packet_without_pn(xqc_connection_t *conn, xqc_path_ctx_t *path, xqc_ conn, packet_out->po_used_size, sent, xqc_pkt_type_2_str(packet_out->po_pkt.pkt_type)); if (sent > 0) { + xqc_conn_log_sent_packet(conn, packet_out, xqc_monotonic_timestamp()); xqc_log_event(conn->log, TRA_PACKET_SENT, packet_out); } return sent; @@ -1851,6 +1917,8 @@ xqc_send_packet_with_pn(xqc_connection_t *conn, xqc_path_ctx_t *path, xqc_packet /* deliver packet to send control */ xqc_pn_ctl_t *pn_ctl = xqc_get_pn_ctl(conn, path); pn_ctl->ctl_packet_number[packet_out->po_pkt.pkt_pns]++; + + 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; } @@ -1902,7 +1970,11 @@ xqc_process_packet_with_pn(xqc_connection_t *conn, xqc_path_ctx_t *path, xqc_pac { ssize_t 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|"); + 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; } @@ -1990,8 +2062,7 @@ xqc_conn_schedule_packets_to_paths(xqc_connection_t *conn) 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) - { + if (conn->conn_settings.mp_enable_reinjection & XQC_REINJ_UNACK_BEFORE_SCHED) { xqc_conn_reinject_unack_packets(conn, XQC_REINJ_UNACK_BEFORE_SCHED); } @@ -2002,8 +2073,7 @@ xqc_conn_schedule_packets_to_paths(xqc_connection_t *conn) xqc_conn_check_path_utilization(conn); xqc_conn_schedule_end(conn); - if (conn->conn_settings.mp_enable_reinjection & XQC_REINJ_UNACK_AFTER_SCHED) - { + if (conn->conn_settings.mp_enable_reinjection & XQC_REINJ_UNACK_AFTER_SCHED) { xqc_conn_reinject_unack_packets(conn, XQC_REINJ_UNACK_AFTER_SCHED); } @@ -2139,83 +2209,163 @@ xqc_path_send_ping_to_probe(xqc_path_ctx_t *path, xqc_pkt_num_space_t pns, } void -xqc_path_send_one_or_two_ack_elicit_pkts(xqc_path_ctx_t *path, xqc_pkt_num_space_t pns) +xqc_conn_send_probe_pkt(xqc_connection_t *c, xqc_path_ctx_t *path, + xqc_packet_out_t *packet_out) { - xqc_connection_t *c = path->parent_conn; - xqc_log(c->log, XQC_LOG_DEBUG, "|send two ack-eliciting pkts|path:%ui|pns:%d|", path->path_id, pns); + xqc_reinjection_mode_t mode; - xqc_packet_out_t *packet_out; - xqc_list_head_t *pos, *next; - xqc_int_t ret; - xqc_int_t probe_num = XQC_CONN_PTO_PKT_CNT_MAX; - xqc_bool_t find_hsd = XQC_FALSE; + mode = c->conn_settings.mp_enable_reinjection & XQC_REINJ_UNACK_BEFORE_SCHED; - if ((c->conn_type == XQC_CONN_TYPE_SERVER) && !(c->conn_flag & XQC_CONN_FLAG_HANDSHAKE_DONE_ACKED)) { - find_hsd = XQC_TRUE; + 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(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(packet_out->po_frame_types)); + } } - /* if only one packet is in pns unacked list, this loop will try to send this packet again */ - while (probe_num > 0) { - xqc_list_for_each_safe(pos, next, &c->conn_send_queue->sndq_unacked_packets[pns]) { - packet_out = xqc_list_entry(pos, xqc_packet_out_t, po_list); + xqc_send_ctl_decrease_inflight(c, packet_out); + xqc_send_queue_copy_to_probe(packet_out, c->conn_send_queue, path); - if (xqc_send_ctl_indirectly_ack_or_drop_po(c, packet_out)) { - continue; - } + packet_out->po_flag |= XQC_POF_TLP; - if (!xqc_packet_out_can_pto_probe(packet_out, path->path_id)) { - continue; - } + if (packet_out->po_frame_types & XQC_FRAME_BIT_DATAGRAM) { + path->path_send_ctl->ctl_lost_dgram_cnt++; + } +} + +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; + xqc_bool_t send_hsd_next; + + 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; + send_hsd_next = 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 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)) + { + 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_frame_types & XQC_FRAME_BIT_DATAGRAM - && c->conn_settings.datagram_force_retrans_on))) + if (XQC_IS_ACK_ELICITING(packet_out->po_frame_types) + && (XQC_NEED_REPAIR(packet_out->po_frame_types) + || (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 (find_hsd && !(packet_out->po_frame_types & XQC_FRAME_BIT_HANDSHAKE_DONE)) { - continue; + 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; } - packet_out->po_flag |= XQC_POF_TLP; + continue; + } - 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(packet_out->po_frame_types), - xqc_conn_state_2_str(c->conn_state)); + xqc_conn_send_probe_pkt(c, path, packet_out); + packet_out_last_sent = packet_out; - xqc_send_ctl_decrease_inflight(c, packet_out); - xqc_send_queue_copy_to_probe(packet_out, c->conn_send_queue, path); + if (--probe_num == 0) { + break; + } - if (packet_out->po_frame_types & XQC_FRAME_BIT_DATAGRAM) { - path->path_send_ctl->ctl_lost_dgram_cnt++; - } + /* 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; - if (--probe_num == 0) { - break; - } + /* try to send the first ack-eliciting pkt do not contain + HSK_DONE frame */ + if (packet_out_later_send) { + 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 (find_hsd) { - find_hsd = XQC_FALSE; - break; + if (--probe_num == 0) { + break; + } } } } + } - /* no data found in PTO pns, break and send PING */ - if (XQC_CONN_PTO_PKT_CNT_MAX == probe_num) { - if (find_hsd) { - find_hsd = XQC_FALSE; - } else { - 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); + xqc_conn_send_probe_pkt(c, path, packet_out_last_sent); + probe_num--; } - } - } - 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--; + } 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--; + } + } } } @@ -2571,6 +2721,7 @@ xqc_conn_info_print(xqc_connection_t *conn, xqc_conn_stats_t *conn_stats) xqc_path_ctx_t *path = NULL; xqc_path_info_t path_info; uint32_t mp_settings = 0; + uint32_t sock_err_flag = 0; if (conn->enable_multipath) { mp_settings |= 1; @@ -2584,9 +2735,21 @@ xqc_conn_info_print(xqc_connection_t *conn, xqc_conn_stats_t *conn_stats) mp_settings |= (1 << 2); } + if (conn->conn_settings.multipath_version == XQC_MULTIPATH_05) { + mp_settings |= (1 << 3); + } + + if (conn->local_settings.multipath_version == XQC_MULTIPATH_05) { + mp_settings |= (1 << 4); + } + + if (conn->remote_settings.multipath_version == XQC_MULTIPATH_05) { + mp_settings |= (1 << 5); + } + /* conn info */ ret = snprintf(buff, buff_size, "%u,%u,%u,%u,%u,%u,%u," - "%u,%u,%u,%u,", + "%u,%u,%u,%u,%u,%u,%u,%"PRIu64",%"PRIu64",%"PRIu64",", mp_settings, conn->create_path_count, conn->validated_path_count, @@ -2597,7 +2760,13 @@ xqc_conn_info_print(xqc_connection_t *conn, xqc_conn_stats_t *conn_stats) conn->dgram_stats.hp_red_dgram_mp, conn->dgram_stats.timer_red_dgram, conn->sched_cc_blocked, - conn->send_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); curr_size += ret; @@ -2605,14 +2774,55 @@ xqc_conn_info_print(xqc_connection_t *conn, xqc_conn_stats_t *conn_stats) goto full; } + /* recv_stats */ + for (i = 0; i < 3; i++) { + + ret = snprintf(buff + curr_size, buff_size - curr_size, + "%u,%u,%u,%"PRIu64",%"PRIu64"," + "%"PRIu64",%"PRIu64",", + (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], + (uint64_t)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,%"PRIu64",%"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-%"PRIu64"," + "%d-%d,%d-%d,%d-%d,%d-%"PRIu64"," "%d-%"PRIu64",%d-%"PRIu64"," "%d-%"PRIu64",%d-%"PRIu64"," "%d-%u,%d-%u," @@ -2620,6 +2830,8 @@ xqc_conn_info_print(xqc_connection_t *conn, xqc_conn_stats_t *conn_stats) "%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, @@ -3007,6 +3219,18 @@ xqc_conn_handshake_confirmed(xqc_connection_t *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_handshake_complete(xqc_connection_t *conn) { @@ -3014,14 +3238,13 @@ xqc_conn_handshake_complete(xqc_connection_t *conn) xqc_list_head_t *pos, *next; xqc_stream_t *stream; + xqc_multipath_version_t mp_version_ret; /* update flow control */ - conn->conn_flow_ctl.fc_max_data_can_send = conn->remote_settings.max_data; - conn->conn_flow_ctl.fc_max_streams_bidi_can_send = conn->remote_settings.max_streams_bidi; - conn->conn_flow_ctl.fc_max_streams_uni_can_send = conn->remote_settings.max_streams_uni; + 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_set_flow_ctl(stream); + xqc_stream_update_flow_ctl(stream); } /* determine multipath mode */ @@ -3034,6 +3257,17 @@ xqc_conn_handshake_complete(xqc_connection_t *conn) conn->conn_flag |= XQC_CONN_FLAG_PMTUD_PROBING; xqc_timer_unset(&conn->conn_timer_manager, XQC_TIMER_PMTUD_PROBING); } + + mp_version_ret = xqc_conn_multipath_version_negotiation(conn); + if (mp_version_ret == XQC_ERR_MULTIPATH_VERSION) { + xqc_log(conn->log, XQC_LOG_WARN, "|multipath_version_negotiation err|"); + conn->enable_multipath = 0; + } + conn->conn_settings.multipath_version = mp_version_ret; + + if (conn->enable_multipath) { + conn->conn_flag |= XQC_CONN_FLAG_MP_WAIT_SCID; + } /* conn's handshake is complete when TLS stack has reported handshake complete */ conn->conn_flag |= XQC_CONN_FLAG_HANDSHAKE_COMPLETED; @@ -3149,8 +3383,11 @@ xqc_conn_process_undecrypt_packet_in(xqc_connection_t *conn, xqc_encrypt_level_t packet_in = xqc_list_entry(pos, xqc_packet_in_t, pi_list); xqc_log(conn->log, XQC_LOG_DEBUG, "|delay|undecrypt_count:%ud|encrypt_level:%d|", conn->undecrypt_count[encrypt_level], encrypt_level); - +#ifdef XQC_NO_PID_PACKET_PROCESS ret = xqc_conn_process_packet(conn, packet_in->buf, packet_in->buf_size, packet_in->pkt_recv_time); +#else + ret = xqc_conn_process_packet(conn, packet_in->buf, packet_in->buf_size, XQC_UNKNOWN_PATH_ID, packet_in->pkt_recv_time); +#endif if (ret) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_packet_process error|ret:%d|", ret); return ret; @@ -3359,11 +3596,21 @@ 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) { + //TODO: MPQUIC fix migration + path = xqc_conn_find_path_by_path_id(c, packet_in->pi_path_id); + + } else { + path = c->conn_initial_path; + } + - xqc_path_ctx_t *path = xqc_conn_find_path_by_path_id(c, packet_in->pi_path_id); if (path == NULL) { return; } + xqc_pn_ctl_t *pn_ctl = xqc_get_pn_ctl(c, path); xqc_send_ctl_t *send_ctl = path->path_send_ctl; @@ -3628,11 +3875,37 @@ xqc_conn_tolerant_error(xqc_int_t ret) 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; +} + +#ifdef XQC_NO_PID_PACKET_PROCESS +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) +#else 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) + const unsigned char *packet_in_buf, size_t packet_in_size, + uint64_t path_id, xqc_usec_t recv_time) +#endif { - xqc_int_t ret = XQC_ERROR; + 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 */ @@ -3647,20 +3920,26 @@ xqc_conn_process_packet(xqc_connection_t *c, 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); +#ifndef XQC_NO_PID_PACKET_PROCESS + packet_in->pi_path_id = path_id; +#else + packet_in->pi_path_id = XQC_UNKNOWN_PATH_ID; +#endif /* 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) { - if (XQC_OK != (ret = xqc_conn_on_pkt_processed(c, packet_in, recv_time))) { - xqc_log(c->log, XQC_LOG_ERROR, "|on_pkt_process error|ret:%d|", ret); - } + 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; - - return XQC_OK; + ret = XQC_OK; + goto end; } /* error occurred or read state is error */ @@ -3675,16 +3954,32 @@ xqc_conn_process_packet(xqc_connection_t *c, pos = packet_in->last; xqc_log_event(c->log, TRA_PACKET_RECEIVED, packet_in); } - - return XQC_OK; +end: + return ret; } - +#ifdef XQC_NO_PID_PACKET_PROCESS void -xqc_conn_process_packet_recved_path(xqc_connection_t *conn, xqc_cid_t *scid, +xqc_conn_process_packet_recved_path(xqc_connection_t *conn, xqc_cid_t *scid, size_t packet_in_size, xqc_usec_t recv_time) +#else +void +xqc_conn_process_packet_recved_path(xqc_connection_t *conn, xqc_cid_t *scid, + uint64_t path_id, size_t packet_in_size, xqc_usec_t recv_time) +#endif { - xqc_path_ctx_t *path = xqc_conn_find_path_by_scid(conn, scid); + xqc_path_ctx_t *path = NULL; + if (conn->enable_multipath) { + path = xqc_conn_find_path_by_scid(conn, scid); +#ifndef XQC_NO_PID_PACKET_PROCESS + if (path == NULL && path_id != XQC_UNKNOWN_PATH_ID) { + path = xqc_conn_find_path_by_path_id(conn, path_id); + } +#endif + } else { + path = conn->conn_initial_path; + } + if (path == NULL) { xqc_log(conn->log, XQC_LOG_INFO, "|ignore unknown path|scid:%s|", xqc_scid_str(scid)); return; @@ -3805,17 +4100,25 @@ xqc_conn_destroy_cids(xqc_connection_t *conn) xqc_int_t xqc_conn_try_add_new_conn_id(xqc_connection_t *conn, uint64_t retire_prior_to) { - if ((xqc_conn_is_handshake_confirmed(conn)) - && (conn->scid_set.cid_set.unused_cnt == 0) - && (conn->scid_set.cid_set.used_cnt < conn->remote_settings.active_connection_id_limit)) - { - xqc_int_t 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; + uint64_t active_cid_cnt = conn->scid_set.cid_set.unused_cnt + conn->scid_set.cid_set.used_cnt; +#ifdef XQC_NO_PID_PACKET_PROCESS + uint64_t unused_limit = 1; +#else + uint64_t unused_limit = conn->enable_multipath ? 2 : 1; +#endif + if (xqc_conn_is_handshake_confirmed(conn)) { + while (active_cid_cnt < conn->remote_settings.active_connection_id_limit + && conn->scid_set.cid_set.unused_cnt < unused_limit) + { + xqc_int_t 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; + } + active_cid_cnt++; } } - + return XQC_OK; } @@ -3970,6 +4273,12 @@ xqc_conn_set_cid_retired_ts(xqc_connection_t *conn, xqc_cid_inner_t *inner_cid) return ret; } + xqc_log(conn->log, XQC_LOG_DEBUG, + "|retired|cid:%s|seq:%ui|len:%d|", + xqc_scid_str(&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); @@ -4243,6 +4552,7 @@ xqc_conn_set_remote_transport_params(xqc_connection_t *conn, settings->active_connection_id_limit = params->active_connection_id_limit; settings->enable_multipath = params->enable_multipath; + settings->multipath_version = params->multipath_version; settings->max_datagram_frame_size = params->max_datagram_frame_size; return XQC_OK; @@ -4279,6 +4589,7 @@ xqc_conn_get_local_transport_params(xqc_connection_t *conn, xqc_transport_params 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->max_datagram_frame_size = settings->max_datagram_frame_size; /* set other transport parameters */ @@ -4402,6 +4713,7 @@ xqc_conn_tls_transport_params_cb(const uint8_t *tp, size_t len, void *user_data) xqc_log(conn->log, XQC_LOG_DEBUG, "|1RTT_transport_params|max_datagram_frame_size:%ud|", conn->remote_settings.max_datagram_frame_size); + /* save no crypto flag */ if (params.no_crypto == 1) { conn->remote_settings.no_crypto = 1; @@ -4440,6 +4752,30 @@ xqc_conn_tls_transport_params_cb(const uint8_t *tp, size_t len, void *user_data) } } + 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) + { + uint8_t tp_buf[XQC_MAX_TRANSPORT_PARAM_BUF_LEN] = {0}; + size_t tp_len = 0; + conn->local_settings.multipath_version = params.multipath_version; + 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}; @@ -4557,18 +4893,6 @@ xqc_settings_copy_from_transport_params(xqc_trans_settings_t *dest, dest->max_datagram_frame_size = src->max_datagram_frame_size; } -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_set_early_remote_transport_params(xqc_connection_t *conn, const xqc_transport_params_t *params) @@ -4818,7 +5142,7 @@ xqc_conn_get_max_pto(xqc_connection_t *conn) } xqc_usec_t -xqc_conn_get_min_srtt(xqc_connection_t *conn) +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; @@ -4830,12 +5154,35 @@ xqc_conn_get_min_srtt(xqc_connection_t *conn) 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) { diff --git a/src/transport/xqc_conn.h b/src/transport/xqc_conn.h index c9e8b4f2b..2700a8d6d 100644 --- a/src/transport/xqc_conn.h +++ b/src/transport/xqc_conn.h @@ -24,6 +24,9 @@ #define XQC_MAX_DATAGRAM_REDUNDANCY 2 #define XQC_MIN_DATAGRAM_REDUNDANT_PROBE_INTERVAL 30000 /* 30ms min probing interval */ +#define XQC_FC_INIT_RTT 60000 +#define XQC_MIN_RECV_WINDOW (63000) /* ~ 1MBps when RTT = 60ms */ +#define XQC_MIN_STANDBY_RPOBE_TIMEOUT 500 /* 500ms */ #define XQC_TOKEN_EXPIRE_DELTA (7 * 24 * 60 * 60) /* expire in N seconds */ #define XQC_TOKEN_UPDATE_DELTA (XQC_TOKEN_EXPIRE_DELTA / 2) /* early update */ @@ -120,7 +123,7 @@ typedef enum { XQC_CONN_FLAG_HANDSHAKE_CONFIRMED_SHIFT, XQC_CONN_FLAG_HANDSHAKE_DONE_ACKED_SHIFT, XQC_CONN_FLAG_ADDR_VALIDATED_SHIFT, - XQC_CONN_FLAG_NEW_CID_RECEIVED_SHIFT, + XQC_CONN_FLAG_NEW_CID_ACKED_SHIFT, XQC_CONN_FLAG_LINGER_CLOSING_SHIFT, XQC_CONN_FLAG_RETRY_RECVD_SHIFT, XQC_CONN_FLAG_TLS_HSK_COMPLETED_SHIFT, @@ -133,6 +136,9 @@ typedef enum { XQC_CONN_FLAG_PMTUD_PROBING_SHIFT, XQC_CONN_FLAG_NO_DGRAM_NOTIFIED_SHIFT, XQC_CONN_FLAG_DGRAM_MSS_NOTIFY_SHIFT, + XQC_CONN_FLAG_MP_WAIT_SCID_SHIFT, + XQC_CONN_FLAG_MP_WAIT_DCID_SHIFT, + XQC_CONN_FLAG_MP_READY_NOTIFY_SHIFT, XQC_CONN_FLAG_SHIFT_NUM, } xqc_conn_flag_shift_t; @@ -165,7 +171,7 @@ typedef enum { XQC_CONN_FLAG_HANDSHAKE_CONFIRMED = 1ULL << XQC_CONN_FLAG_HANDSHAKE_CONFIRMED_SHIFT, XQC_CONN_FLAG_HANDSHAKE_DONE_ACKED = 1ULL << XQC_CONN_FLAG_HANDSHAKE_DONE_ACKED_SHIFT, XQC_CONN_FLAG_ADDR_VALIDATED = 1ULL << XQC_CONN_FLAG_ADDR_VALIDATED_SHIFT, - XQC_CONN_FLAG_NEW_CID_RECEIVED = 1ULL << XQC_CONN_FLAG_NEW_CID_RECEIVED_SHIFT, + XQC_CONN_FLAG_NEW_CID_ACKED = 1ULL << XQC_CONN_FLAG_NEW_CID_ACKED_SHIFT, XQC_CONN_FLAG_LINGER_CLOSING = 1ULL << XQC_CONN_FLAG_LINGER_CLOSING_SHIFT, XQC_CONN_FLAG_RETRY_RECVD = 1ULL << XQC_CONN_FLAG_RETRY_RECVD_SHIFT, XQC_CONN_FLAG_TLS_HSK_COMPLETED = 1ULL << XQC_CONN_FLAG_TLS_HSK_COMPLETED_SHIFT, @@ -178,6 +184,9 @@ typedef enum { XQC_CONN_FLAG_PMTUD_PROBING = 1ULL << XQC_CONN_FLAG_PMTUD_PROBING_SHIFT, XQC_CONN_FLAG_NO_DGRAM_NOTIFIED = 1ULL << XQC_CONN_FLAG_NO_DGRAM_NOTIFIED_SHIFT, XQC_CONN_FLAG_DGRAM_MSS_NOTIFY = 1ULL << XQC_CONN_FLAG_DGRAM_MSS_NOTIFY_SHIFT, + XQC_CONN_FLAG_MP_WAIT_SCID = 1ULL << XQC_CONN_FLAG_MP_WAIT_SCID_SHIFT, + XQC_CONN_FLAG_MP_WAIT_DCID = 1ULL << XQC_CONN_FLAG_MP_WAIT_DCID_SHIFT, + XQC_CONN_FLAG_MP_READY_NOTIFY = 1ULL << XQC_CONN_FLAG_MP_READY_NOTIFY_SHIFT, } xqc_conn_flag_t; @@ -200,6 +209,7 @@ typedef struct { uint64_t active_connection_id_limit; uint64_t no_crypto; uint64_t enable_multipath; + xqc_multipath_version_t multipath_version; uint16_t max_datagram_frame_size; } xqc_trans_settings_t; @@ -351,9 +361,7 @@ struct xqc_connection_s { uint32_t create_path_count; uint32_t validated_path_count; uint32_t active_path_count; - uint32_t in_use_active_path_count; - uint8_t switch_initial; - + const xqc_scheduler_callback_t *scheduler_callback; void *scheduler; @@ -388,7 +396,13 @@ struct xqc_connection_s { uint32_t hp_red_dgram; uint32_t hp_red_dgram_mp; uint32_t timer_red_dgram; - } dgram_stats; + } dgram_stats; + + struct { + uint64_t send_bytes; + uint64_t reinjected_bytes; + uint64_t recv_bytes; + } stream_stats; xqc_gp_timer_id_t dgram_probe_timer; xqc_var_buf_t *last_dgram; @@ -405,6 +419,30 @@ struct xqc_connection_s { /* cc blocking stats */ uint32_t sched_cc_blocked; uint32_t send_cc_blocked; + + /* receved pkts stats */ + struct { + xqc_pkt_type_t pkt_types[3]; + xqc_frame_type_bit_t pkt_frames[3]; + uint32_t pkt_size[3]; + uint32_t pkt_udp_size[3]; + uint64_t pkt_err[3]; + xqc_usec_t pkt_timestamp[3]; + xqc_packet_number_t pkt_pn[3]; + uint8_t curr_index; + uint32_t conn_rcvd_pkts; + uint32_t conn_udp_pkts; + } rcv_pkt_stats; + + struct { + xqc_pkt_type_t pkt_types[3]; + xqc_frame_type_bit_t pkt_frames[3]; + uint32_t pkt_size[3]; + xqc_usec_t pkt_timestamp[3]; + xqc_packet_number_t pkt_pn[3]; + uint8_t curr_index; + uint32_t conn_sent_pkts; + } snd_pkt_stats; }; const char *xqc_conn_flag_2_str(xqc_conn_flag_t conn_flag); @@ -512,11 +550,21 @@ xqc_conn_should_ack(xqc_connection_t *conn) } /* process an UDP datagram */ +#ifdef XQC_NO_PID_PACKET_PROCESS 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); +#else +xqc_int_t xqc_conn_process_packet(xqc_connection_t *c, const unsigned char *packet_in_buf, + size_t packet_in_size, uint64_t path_id, xqc_usec_t recv_time); +#endif -void xqc_conn_process_packet_recved_path(xqc_connection_t *conn, xqc_cid_t *scid, +#ifdef XQC_NO_PID_PACKET_PROCESS +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); +#else +void xqc_conn_process_packet_recved_path(xqc_connection_t *conn, xqc_cid_t *scid, + uint64_t path_id, size_t packet_in_size, xqc_usec_t recv_time); +#endif xqc_int_t xqc_conn_check_handshake_complete(xqc_connection_t *conn); @@ -564,7 +612,8 @@ xqc_usec_t xqc_conn_get_max_pto(xqc_connection_t *conn); void xqc_conn_ptmud_probing(xqc_connection_t *conn); /* 用于流控 */ -xqc_usec_t xqc_conn_get_min_srtt(xqc_connection_t *conn); +xqc_usec_t xqc_conn_get_min_srtt(xqc_connection_t *conn, xqc_bool_t available_only); +xqc_usec_t xqc_conn_get_max_srtt(xqc_connection_t *conn); void xqc_conn_check_app_limit(xqc_connection_t *conn); diff --git a/src/transport/xqc_engine.c b/src/transport/xqc_engine.c index 219d8bf7b..7f8b2a6d7 100644 --- a/src/transport/xqc_engine.c +++ b/src/transport/xqc_engine.c @@ -681,6 +681,7 @@ xqc_engine_process_conn(xqc_connection_t *conn, xqc_usec_t now) conn, xqc_conn_state_2_str(conn->conn_state), xqc_conn_flag_2_str(conn->conn_flag), now); int ret; + xqc_bool_t wait_scid, wait_dcid; xqc_conn_timer_expire(conn, now); @@ -745,19 +746,10 @@ xqc_engine_process_conn(xqc_connection_t *conn, xqc_usec_t now) XQC_CHECK_IMMEDIATE_CLOSE(); if (xqc_conn_should_ack(conn)) { - if (conn->enable_multipath == XQC_CONN_MULTIPATH_MULTIPLE_PNS) { - ret = xqc_write_ack_mp_to_packets(conn); - if (ret) { - xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_ack_mp_to_packets error|"); - XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR); - } - - } else { - ret = xqc_write_ack_to_packets(conn); - if (ret) { - xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_ack_to_packets error|"); - XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR); - } + ret = xqc_write_ack_or_mp_ack_to_packets(conn); + if (ret) { + xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_ack_or_mp_ack_to_packets error|"); + XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR); } } XQC_CHECK_IMMEDIATE_CLOSE(); @@ -767,15 +759,33 @@ xqc_engine_process_conn(xqc_connection_t *conn, xqc_usec_t now) xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_conn_try_add_new_conn_id error|"); } + if (conn->enable_multipath) { + + if (conn->conn_flag & XQC_CONN_FLAG_MP_WAIT_SCID) { + if (conn->conn_flag & XQC_CONN_FLAG_NEW_CID_ACKED) { + conn->conn_flag &= ~XQC_CONN_FLAG_MP_WAIT_SCID; + conn->conn_flag &= ~XQC_CONN_FLAG_NEW_CID_ACKED; + conn->conn_flag |= XQC_CONN_FLAG_MP_READY_NOTIFY; + } + } + + if (conn->conn_flag & XQC_CONN_FLAG_MP_WAIT_DCID + && !(conn->conn_flag & XQC_CONN_FLAG_MP_WAIT_SCID)) + { + conn->conn_flag &= ~XQC_CONN_FLAG_MP_WAIT_DCID; + conn->conn_flag |= XQC_CONN_FLAG_MP_READY_NOTIFY; + } + } + /* for multi-path */ - if ((conn->conn_flag & XQC_CONN_FLAG_NEW_CID_RECEIVED) + if ((conn->conn_flag & XQC_CONN_FLAG_MP_READY_NOTIFY) && xqc_conn_check_unused_cids(conn) == XQC_OK) { if (conn->transport_cbs.ready_to_create_path_notify) { conn->transport_cbs.ready_to_create_path_notify(&conn->scid_set.user_scid, xqc_conn_get_user_data(conn)); } - conn->conn_flag &= ~XQC_CONN_FLAG_NEW_CID_RECEIVED; + conn->conn_flag &= ~XQC_CONN_FLAG_MP_READY_NOTIFY; } if (XQC_UNLIKELY(conn->conn_flag & XQC_CONN_FLAG_PING)) { @@ -1097,12 +1107,21 @@ xqc_engine_process_sr_pkt(xqc_engine_t *engine, const unsigned char *buf, * Pass received UDP packet payload into xquic engine. * @param recv_time UDP packet received time in microsecond */ +#ifdef XQC_NO_PID_PACKET_PROCESS xqc_int_t xqc_engine_packet_process(xqc_engine_t *engine, const unsigned char *packet_in_buf, size_t packet_in_size, const struct sockaddr *local_addr, socklen_t local_addrlen, const struct sockaddr *peer_addr, socklen_t peer_addrlen, xqc_usec_t recv_time, void *user_data) +#else +xqc_int_t +xqc_engine_packet_process(xqc_engine_t *engine, + const unsigned char *packet_in_buf, size_t packet_in_size, + const struct sockaddr *local_addr, socklen_t local_addrlen, + const struct sockaddr *peer_addr, socklen_t peer_addrlen, + uint64_t path_id, xqc_usec_t recv_time, void *user_data) +#endif { xqc_int_t ret; xqc_connection_t *conn = NULL; @@ -1215,7 +1234,7 @@ xqc_engine_packet_process(xqc_engine_t *engine, ret = xqc_conn_send_path_challenge(conn, path); if (ret == XQC_OK) { - xqc_log(conn->log, XQC_LOG_INFO, "|REBINDING|path:%ui|send PATH_CHALLENGE|", path->path_id); + xqc_log(conn->log, XQC_LOG_INFO, "|REBINDING|path:%ui|send PATH_CHALLENGE|addr:%s|", path->path_id, xqc_path_addr_str(path)); path->rebinding_count++; xqc_usec_t pto = xqc_conn_get_max_pto(conn); xqc_timer_set(&path->path_send_ctl->path_timer_manager, @@ -1238,7 +1257,14 @@ xqc_engine_packet_process(xqc_engine_t *engine, } /* process packets */ +#ifdef XQC_NO_PID_PACKET_PROCESS ret = xqc_conn_process_packet(conn, packet_in_buf, packet_in_size, recv_time); +#else + ret = xqc_conn_process_packet(conn, packet_in_buf, packet_in_size, path_id, recv_time); +#endif + + conn->rcv_pkt_stats.conn_udp_pkts++; + if (ret) { xqc_log(engine->log, XQC_LOG_ERROR, "|fail to process packets|conn:%p|ret:%d|", conn, ret); XQC_CONN_ERR(conn, TRA_FRAME_ENCODING_ERROR); @@ -1262,8 +1288,11 @@ xqc_engine_packet_process(xqc_engine_t *engine, goto after_process; } } - +#ifdef XQC_NO_PID_PACKET_PROCESS xqc_conn_process_packet_recved_path(conn, &scid, packet_in_size, recv_time); +#else + xqc_conn_process_packet_recved_path(conn, &scid, path_id, packet_in_size, recv_time); +#endif xqc_timer_set(&conn->conn_timer_manager, XQC_TIMER_CONN_IDLE, recv_time, xqc_conn_get_idle_timeout(conn) * 1000); diff --git a/src/transport/xqc_frame.c b/src/transport/xqc_frame.c index 5e23d9645..d3a41fd80 100644 --- a/src/transport/xqc_frame.c +++ b/src/transport/xqc_frame.c @@ -269,13 +269,55 @@ xqc_process_frames(xqc_connection_t *conn, xqc_packet_in_t *packet_in) ret = xqc_process_datagram_frame(conn, packet_in); break; case 0xbaba00 ... 0xbaba01: - ret = xqc_process_ack_mp_frame(conn, packet_in); + if (conn->conn_settings.multipath_version == XQC_MULTIPATH_04) { + ret = xqc_process_ack_mp_frame(conn, packet_in); + } else { + xqc_log(conn->log, XQC_LOG_ERROR, "|receive wrong mp version mp_ack frame or cannot process frame in mp version 04|"); + ret = -XQC_EMP_INVALID_MP_VERTION; + } + break; + case 0x15228c00 ... 0x15228c01: + if (conn->conn_settings.multipath_version == XQC_MULTIPATH_05) { + ret = xqc_process_ack_mp_frame(conn, packet_in); + + } else { + xqc_log(conn->log, XQC_LOG_ERROR, "|receive wrong mp version mp_ack frame or cannot process frame in mp version 05|"); + ret = -XQC_EMP_INVALID_MP_VERTION; + } break; case 0xbaba05: - ret = xqc_process_path_abandon_frame(conn, packet_in); + if (conn->conn_settings.multipath_version == XQC_MULTIPATH_04) { + ret = xqc_process_path_abandon_frame(conn, packet_in); + } else { + xqc_log(conn->log, XQC_LOG_ERROR, "|receive wrong mp version path_abandon frame or cannot process frame in mp version 04|"); + ret = -XQC_EMP_INVALID_MP_VERTION; + } + break; + case 0x15228c05: + if (conn->conn_settings.multipath_version == XQC_MULTIPATH_05) { + ret = xqc_process_path_abandon_frame(conn, packet_in); + + } else { + xqc_log(conn->log, XQC_LOG_ERROR, "|receive wrong mp version path_abandon frame or cannot process frame in mp version 05|"); + ret = -XQC_EMP_INVALID_MP_VERTION; + } break; case 0xbaba06: - ret = xqc_process_path_status_frame(conn, packet_in); + if (conn->conn_settings.multipath_version == XQC_MULTIPATH_04) { + ret = xqc_process_path_status_frame(conn, packet_in); + } else { + xqc_log(conn->log, XQC_LOG_ERROR, "|receive wrong mp version path_status frame or cannot process frame in mp version 04|"); + ret = -XQC_EMP_INVALID_MP_VERTION; + } + break; + case 0x15228c06: + if (conn->conn_settings.multipath_version == XQC_MULTIPATH_05) { + ret = xqc_process_path_status_frame(conn, packet_in); + + } else { + xqc_log(conn->log, XQC_LOG_ERROR, "|receive wrong mp version path_status frame or cannot process frame in mp version 05|"); + ret = -XQC_EMP_INVALID_MP_VERTION; + } break; default: xqc_log(conn->log, XQC_LOG_ERROR, "|unknown frame type|"); @@ -345,8 +387,8 @@ xqc_process_stream_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in) stream_type = xqc_get_stream_type(stream_id); - xqc_log(conn->log, XQC_LOG_DEBUG, "|offset:%ui|data_length:%ud|fin:%ud|stream_id:%ui|", - stream_frame->data_offset, stream_frame->data_length, stream_frame->fin, stream_id); + xqc_log(conn->log, XQC_LOG_DEBUG, "|offset:%ui|data_length:%ud|fin:%ud|stream_id:%ui|path:%ui|", + stream_frame->data_offset, stream_frame->data_length, stream_frame->fin, stream_id, packet_in->pi_path_id); stream = xqc_find_stream_by_id(stream_id, conn->streams_hash); if (!stream) { @@ -365,6 +407,10 @@ xqc_process_stream_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in) } } + conn->stream_stats.recv_bytes += stream_frame->data_length; + + xqc_stream_path_metrics_on_recv(conn, stream, packet_in); + if (packet_in->pi_path_id < XQC_MAX_PATHS_COUNT) { stream->paths_info[packet_in->pi_path_id].path_recv_bytes += stream_frame->data_length; } @@ -476,7 +522,6 @@ xqc_process_stream_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_in) xqc_stream_ready_to_read(stream); } - xqc_stream_path_metrics_on_recv(conn, stream, packet_in); if (packet_in->pi_path_id < XQC_MAX_PATHS_COUNT) { stream->paths_info[packet_in->pi_path_id].path_recv_effective_bytes += stream_frame->data_length; } @@ -819,10 +864,10 @@ xqc_process_retire_conn_id_frame(xqc_connection_t *conn, xqc_packet_in_t *packet } /* TODO: 如果对应 “Active” Path 则需要替换 CID */ - xqc_path_ctx_t *path = xqc_conn_find_path_by_scid(conn, &inner_cid->cid); - if (path != NULL) { - xqc_log(conn->log, XQC_LOG_DEBUG, "|path:%ui|state:%d|", path->path_id, path->path_state); - } + // xqc_path_ctx_t *path = xqc_conn_find_path_by_scid(conn, &inner_cid->cid); + // if (path != NULL) { + // xqc_log(conn->log, XQC_LOG_DEBUG, "|path:%ui|state:%d|", path->path_id, path->path_state); + // } return XQC_OK; } @@ -1047,7 +1092,7 @@ xqc_process_stream_data_blocked_frame(xqc_connection_t *conn, xqc_packet_in_t *p stream->stream_flow_ctl.fc_max_stream_data_can_recv = stream->stream_data_in.next_read_offset + stream->stream_flow_ctl.fc_stream_recv_window_size; - ret = xqc_write_max_stream_data_to_packet(conn, stream_id, stream->stream_flow_ctl.fc_max_stream_data_can_recv); + ret = xqc_write_max_stream_data_to_packet(conn, stream_id, stream->stream_flow_ctl.fc_max_stream_data_can_recv, XQC_PTYPE_SHORT_HEADER); if (ret != XQC_OK) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_max_stream_data_to_packet error|"); return ret; @@ -1316,21 +1361,40 @@ xqc_process_path_challenge_frame(xqc_connection_t *conn, xqc_packet_in_t *packet return ret; } - xqc_path_ctx_t *path = xqc_conn_find_path_by_scid(conn, &packet_in->pi_pkt.pkt_dcid); + //TODO: MPQUIC fix migration + xqc_path_ctx_t *path = NULL; + if (conn->enable_multipath) { + path = xqc_conn_find_path_by_scid(conn, &packet_in->pi_pkt.pkt_dcid); + + } else { + path = conn->conn_initial_path; + } + if (path == NULL) { - /* try to create new path */ - path = xqc_conn_create_path_inner(conn, &packet_in->pi_pkt.pkt_dcid, NULL); - if (path == NULL) { - xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_conn_create_path_inner err|"); - return -XQC_EMP_CREATE_PATH; - } + if (conn->conn_type == XQC_CONN_TYPE_SERVER) { + /* try to create new path */ + path = xqc_conn_create_path_inner(conn, &packet_in->pi_pkt.pkt_dcid, NULL, XQC_APP_PATH_STATUS_AVAILABLE); + if (path == NULL) { + xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_conn_create_path_inner err|"); + return -XQC_EMP_CREATE_PATH; + } + packet_in->pi_path_id = path->path_id; + conn->validating_path_id = path->path_id; + conn->conn_flag |= XQC_CONN_FLAG_RECV_NEW_PATH; - conn->validating_path_id = path->path_id; - conn->conn_flag |= XQC_CONN_FLAG_RECV_NEW_PATH; + } else { + xqc_log(conn->log, XQC_LOG_ERROR, + "|no path to challenge|dcid:%s|path_id:%ui|", + xqc_dcid_str(&packet_in->pi_pkt.pkt_dcid), + packet_in->pi_path_id); + return XQC_OK; + } } - xqc_log(conn->log, XQC_LOG_DEBUG, "|path:%ui|state:%d|RECV path_challenge_data:%s|", - path->path_id, path->path_state, path_challenge_data); + xqc_log(conn->log, XQC_LOG_DEBUG, + "|path:%ui|state:%d|RECV path_challenge_data:%s|cid:%s|", + path->path_id, path->path_state, + path_challenge_data, xqc_dcid_str(&packet_in->pi_pkt.pkt_dcid)); ret = xqc_write_path_response_frame_to_packet(conn, path, path_challenge_data); if (ret != XQC_OK) { @@ -1354,12 +1418,22 @@ xqc_process_path_response_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_ return ret; } - xqc_path_ctx_t *path = xqc_conn_find_path_by_scid(conn, &packet_in->pi_pkt.pkt_dcid); - if (path == NULL) { - xqc_log(conn->log, XQC_LOG_ERROR, "|can't find path|pkt_dcid:%s|", xqc_scid_str(&packet_in->pi_pkt.pkt_dcid)); - return -XQC_EMP_PATH_NOT_FOUND; - } + //TODO: MPQUIC fix migration + xqc_path_ctx_t *path = NULL; + if (conn->enable_multipath) { + path = xqc_conn_find_path_by_scid(conn, &packet_in->pi_pkt.pkt_dcid); + if (path == NULL) { + xqc_log(conn->log, XQC_LOG_ERROR, + "|ingnore path response|pkt_dcid:%s|path_id:%ui|", + xqc_scid_str(&packet_in->pi_pkt.pkt_dcid), + packet_in->pi_path_id); + return XQC_OK; + } + } else { + path = conn->conn_initial_path; + } + xqc_log(conn->log, XQC_LOG_DEBUG, "|path:%ui|state:%d|RECV path_response_data:%s|", path->path_id, path->path_state, path_response_data); @@ -1477,7 +1551,7 @@ xqc_process_path_abandon_frame(xqc_connection_t *conn, xqc_packet_in_t *packet_i if (path == NULL) { xqc_log(conn->log, XQC_LOG_WARN, - "|invalid path|dcid_seq_num:%ui|", + "|invalid path|dcid_seq_num:%ui|path_id:%ui|", dcid_seq_num, packet_in->pi_path_id); return XQC_OK; /* ignore */ } diff --git a/src/transport/xqc_frame_parser.c b/src/transport/xqc_frame_parser.c index b4f75b8d9..95e0b4f34 100644 --- a/src/transport/xqc_frame_parser.c +++ b/src/transport/xqc_frame_parser.c @@ -1764,10 +1764,10 @@ xqc_parse_path_response_frame(xqc_packet_in_t *packet_in, unsigned char *data) } /* - * https://datatracker.ietf.org/doc/html/draft-ietf-quic-multipath#name-ack_mp-frame + * https://datatracker.ietf.org/doc/html/draft-ietf-quic-multipath-05#name-ack_mp-frame * * ACK_MP Frame { - * Type (i) = TBD-00..TBD-01 (experiments use 0xbaba00..0xbaba01), + * Type (i) = TBD-00..TBD-01 , * Destination Connection ID Sequence Number (i), * Largest Acknowledged (i), * ACK Delay (i), @@ -1781,12 +1781,22 @@ xqc_parse_path_response_frame(xqc_packet_in_t *packet_in, unsigned char *data) */ ssize_t -xqc_gen_ack_mp_frame(xqc_connection_t *conn, uint64_t path_id, +xqc_gen_ack_mp_frame(xqc_connection_t *conn, uint64_t dcid_seq, xqc_packet_out_t *packet_out, xqc_usec_t now, int ack_delay_exponent, xqc_recv_record_t *recv_record, xqc_usec_t largest_pkt_recv_time, int *has_gap, xqc_packet_number_t *largest_ack) { - uint64_t frame_type = 0xbaba00; + uint64_t frame_type; + if (conn->conn_settings.multipath_version == XQC_MULTIPATH_04) { + frame_type = 0xbaba00; + + } else if (conn->conn_settings.multipath_version == XQC_MULTIPATH_05) { + frame_type = 0x15228c00; + + } else { + return -XQC_EMP_INVALID_MP_VERTION; + } + unsigned char *dst_buf = packet_out->po_buf + packet_out->po_used_size; size_t dst_buf_len = packet_out->po_buf_size - packet_out->po_used_size + XQC_ACK_SPACE; @@ -1831,13 +1841,13 @@ xqc_gen_ack_mp_frame(xqc_connection_t *conn, uint64_t path_id, ack_delay = ack_delay >> ack_delay_exponent; unsigned frame_type_bits = xqc_vint_get_2bit(frame_type); - unsigned path_id_bits = xqc_vint_get_2bit(path_id); + unsigned dcid_seq_bits = xqc_vint_get_2bit(dcid_seq); unsigned lagest_recv_bits = xqc_vint_get_2bit(lagest_recv); unsigned ack_delay_bits = xqc_vint_get_2bit(ack_delay); unsigned first_ack_range_bits = xqc_vint_get_2bit(first_ack_range); need = + xqc_vint_len(frame_type_bits) - + xqc_vint_len(path_id_bits) + + xqc_vint_len(dcid_seq_bits) + xqc_vint_len(lagest_recv_bits) + xqc_vint_len(ack_delay_bits) + 1 /* range_count */ @@ -1850,8 +1860,8 @@ xqc_gen_ack_mp_frame(xqc_connection_t *conn, uint64_t path_id, xqc_vint_write(dst_buf, frame_type, frame_type_bits, xqc_vint_len(frame_type_bits)); dst_buf += xqc_vint_len(frame_type_bits); - xqc_vint_write(dst_buf, path_id, path_id_bits, xqc_vint_len(path_id_bits)); - dst_buf += xqc_vint_len(path_id_bits); + xqc_vint_write(dst_buf, dcid_seq, dcid_seq_bits, xqc_vint_len(dcid_seq_bits)); + dst_buf += xqc_vint_len(dcid_seq_bits); xqc_vint_write(dst_buf, lagest_recv, lagest_recv_bits, xqc_vint_len(lagest_recv_bits)); dst_buf += xqc_vint_len(lagest_recv_bits); @@ -2017,10 +2027,10 @@ xqc_parse_ack_mp_frame(xqc_packet_in_t *packet_in, xqc_connection_t *conn, /* - * https://datatracker.ietf.org/doc/html/draft-ietf-quic-multipath#name-path_abandon-frame + * https://datatracker.ietf.org/doc/html/draft-ietf-quic-multipath-05#name-path_abandon-frame * * PATH_ABANDON Frame { - * Type (i) = TBD-03 (experiments use 0xbaba05), + * Type (i) = TBD-03, * DCID Sequence Number (i), * Error Code (i), * Reason Phrase Length (i), @@ -2031,14 +2041,24 @@ xqc_parse_ack_mp_frame(xqc_packet_in_t *packet_in, xqc_connection_t *conn, */ ssize_t -xqc_gen_path_abandon_frame(xqc_packet_out_t *packet_out, +xqc_gen_path_abandon_frame(xqc_connection_t *conn, xqc_packet_out_t *packet_out, uint64_t dcid_seq_num, uint64_t error_code) { unsigned char *dst_buf = packet_out->po_buf + packet_out->po_used_size; const unsigned char *begin = dst_buf; unsigned need = 0; + uint64_t frame_type; + + if (conn->conn_settings.multipath_version == XQC_MULTIPATH_04) { + frame_type = 0xbaba05; + + } else if (conn->conn_settings.multipath_version == XQC_MULTIPATH_05) { + frame_type = 0x15228c05; + + } else { + return -XQC_EMP_INVALID_MP_VERTION; + } - uint64_t frame_type = 0xbaba05; uint64_t reason_len = 0; uint8_t *reason = NULL; @@ -2137,10 +2157,10 @@ xqc_parse_path_abandon_frame(xqc_packet_in_t *packet_in, /* - * https://datatracker.ietf.org/doc/html/draft-ietf-quic-multipath#name-path_status-frame + * https://datatracker.ietf.org/doc/html/draft-ietf-quic-multipath-05#name-path_status-frame * * PATH_STATUS Frame { - * Type (i) = TBD-03 (experiments use 0xbaba06), + * Type (i) = TBD-03, * DCID Sequence Number (i), * Path Status sequence number (i), * Path Status (i), @@ -2150,7 +2170,8 @@ xqc_parse_path_abandon_frame(xqc_packet_in_t *packet_in, */ ssize_t -xqc_gen_path_status_frame(xqc_packet_out_t *packet_out, +xqc_gen_path_status_frame(xqc_connection_t *conn, + xqc_packet_out_t *packet_out, uint64_t dcid_seq_num, uint64_t path_status_seq_num, uint64_t path_status) { @@ -2158,7 +2179,16 @@ xqc_gen_path_status_frame(xqc_packet_out_t *packet_out, const unsigned char *begin = dst_buf; unsigned need = 0; - uint64_t frame_type = 0xbaba06; + uint64_t frame_type; + if (conn->conn_settings.multipath_version == XQC_MULTIPATH_04) { + frame_type = 0xbaba06; + + } else if (conn->conn_settings.multipath_version == XQC_MULTIPATH_05) { + frame_type = 0x15228c06; + + } else { + return -XQC_EMP_INVALID_MP_VERTION; + } unsigned frame_type_bits = xqc_vint_get_2bit(frame_type); unsigned dcid_seq_num_bits = xqc_vint_get_2bit(dcid_seq_num); diff --git a/src/transport/xqc_frame_parser.h b/src/transport/xqc_frame_parser.h index bd293806b..a663c6ed3 100644 --- a/src/transport/xqc_frame_parser.h +++ b/src/transport/xqc_frame_parser.h @@ -125,13 +125,14 @@ ssize_t xqc_gen_ack_mp_frame(xqc_connection_t *conn, uint64_t path_id, xqc_packe xqc_int_t xqc_parse_ack_mp_frame(xqc_packet_in_t *packet_in, xqc_connection_t *conn, uint64_t *dcid_seq_num, xqc_ack_info_t *ack_info); -ssize_t xqc_gen_path_abandon_frame(xqc_packet_out_t *packet_out, - uint64_t dcid_seq_num, uint64_t error_code); +ssize_t xqc_gen_path_abandon_frame(xqc_connection_t *conn, + xqc_packet_out_t *packet_out, uint64_t dcid_seq_num, uint64_t error_code); xqc_int_t xqc_parse_path_abandon_frame(xqc_packet_in_t *packet_in, uint64_t *dcid_seq_num, uint64_t *error_code); -ssize_t xqc_gen_path_status_frame(xqc_packet_out_t *packet_out, +ssize_t xqc_gen_path_status_frame(xqc_connection_t *conn, + xqc_packet_out_t *packet_out, uint64_t dcid_seq_num, uint64_t path_status_seq_num, uint64_t path_status); diff --git a/src/transport/xqc_multipath.c b/src/transport/xqc_multipath.c index 010a5f94f..2a4c6c776 100644 --- a/src/transport/xqc_multipath.c +++ b/src/transport/xqc_multipath.c @@ -84,8 +84,6 @@ xqc_path_create(xqc_connection_t *conn, xqc_cid_t *scid, xqc_cid_t *dcid) path->path_state = XQC_PATH_STATE_INIT; path->parent_conn = conn; - - path->tra_path_status = XQC_TRA_PATH_STATUS_IN_USE; path->app_path_status = XQC_APP_PATH_STATUS_AVAILABLE; path->app_path_status_send_seq_num = 0; path->app_path_status_recv_seq_num = 0; @@ -135,7 +133,8 @@ xqc_path_create(xqc_connection_t *conn, xqc_cid_t *scid, xqc_cid_t *dcid) xqc_cid_copy(&(path->path_dcid), dcid); } - path->path_id = path->path_scid.cid_seq_num; + //TODO: MPQUIC fix migration + path->path_id = conn->create_path_count; path->path_create_time = xqc_monotonic_timestamp(); path->curr_pkt_out_size = conn->pkt_out_size; path->path_max_pkt_out_size = conn->max_pkt_out_size; @@ -192,7 +191,7 @@ xqc_path_init(xqc_path_ctx_t *path, xqc_connection_t *conn) } /* write path challenge frame & send immediately */ - ret = xqc_write_path_challenge_frame_to_packet(conn, path); + ret = xqc_write_path_challenge_frame_to_packet(conn, path, path->app_path_status == XQC_APP_PATH_STATUS_STANDBY); if (ret != XQC_OK) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_path_challenge_frame_to_packet error|%d|", ret); return ret; @@ -276,17 +275,9 @@ xqc_set_path_state(xqc_path_ctx_t *path, xqc_path_state_t dst_state) if (path->path_state == XQC_PATH_STATE_ACTIVE) { conn->active_path_count--; - - if (path->tra_path_status == XQC_TRA_PATH_STATUS_IN_USE) { - conn->in_use_active_path_count--; - } } else if (dst_state == XQC_PATH_STATE_ACTIVE) { conn->active_path_count++; - - if (path->tra_path_status == XQC_TRA_PATH_STATUS_IN_USE) { - conn->in_use_active_path_count++; - } } path->path_state = dst_state; @@ -374,22 +365,56 @@ xqc_conn_enable_multipath(xqc_connection_t *conn) xqc_log(conn->log, XQC_LOG_ERROR, "|mutlipath is not possible for connections" " with zero-length DCID|"); - XQC_CONN_ERR(conn, TRA_TRANSPORT_PARAMETER_ERROR); + if (conn->conn_settings.multipath_version == XQC_MULTIPATH_04) { + XQC_CONN_ERR(conn, TRA_TRANSPORT_PARAMETER_ERROR); + + } else { + XQC_CONN_ERR(conn, TRA_MP_PROTOCOL_VIOLATION_05); + } return XQC_CONN_NOT_SUPPORT_MULTIPATH; } return XQC_CONN_MULTIPATH_MULTIPLE_PNS; } - return XQC_CONN_NOT_SUPPORT_MULTIPATH; +} +xqc_multipath_version_t +xqc_conn_multipath_version_negotiation(xqc_connection_t *conn) +{ + if (xqc_conn_is_current_mp_version_supported(conn->remote_settings.multipath_version) == XQC_OK && + conn->local_settings.multipath_version == conn->remote_settings.multipath_version) + { + xqc_log(conn->log, XQC_LOG_DEBUG, + "|multipath version negotiation succeed on multipath 0%d|", conn->remote_settings.multipath_version); + return conn->remote_settings.multipath_version; + } + return XQC_ERR_MULTIPATH_VERSION; } +xqc_int_t +xqc_conn_is_current_mp_version_supported(xqc_multipath_version_t mp_version) +{ + xqc_int_t ret; + switch (mp_version) { + case XQC_MULTIPATH_04: + ret = XQC_OK; + break; + case XQC_MULTIPATH_05: + ret = XQC_OK; + break; + default: + ret = -XQC_EMP_INVALID_MP_VERTION; + break; + } + return ret; +} xqc_int_t -xqc_conn_create_path(xqc_engine_t *engine, const xqc_cid_t *scid, uint64_t *new_path_id) +xqc_conn_create_path(xqc_engine_t *engine, const xqc_cid_t *scid, uint64_t *new_path_id, int path_status) { xqc_connection_t *conn = NULL; xqc_path_ctx_t *path = NULL; + xqc_app_path_status_t ps_inner = XQC_APP_PATH_STATUS_AVAILABLE; conn = xqc_engine_conns_hash_find(engine, scid, 's'); if (!conn) { @@ -409,12 +434,24 @@ xqc_conn_create_path(xqc_engine_t *engine, const xqc_cid_t *scid, uint64_t *new_ /* must have at least one available unused scid & dcid */ if (xqc_conn_check_unused_cids(conn) != XQC_OK) { + if (conn->dcid_set.cid_set.unused_cnt == 0) { + conn->conn_flag |= XQC_CONN_FLAG_MP_WAIT_DCID; + } + + if (conn->scid_set.cid_set.unused_cnt == 0) { + conn->conn_flag |= XQC_CONN_FLAG_MP_WAIT_SCID; + } + xqc_log(conn->log, XQC_LOG_WARN, "|don't have available cid for new path|"); return -XQC_EMP_NO_AVAIL_PATH_ID; } - path = xqc_conn_create_path_inner(conn, NULL, NULL); + if (path_status == XQC_APP_PATH_STATUS_STANDBY) { + ps_inner = XQC_APP_PATH_STATUS_STANDBY; + } + + path = xqc_conn_create_path_inner(conn, NULL, NULL, ps_inner); if (path == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_path_create error|"); return -XQC_EMP_CREATE_PATH; @@ -494,7 +531,8 @@ xqc_conn_init_paths_list(xqc_connection_t *conn) conn->conn_initial_path = xqc_conn_create_path_inner(conn, &conn->scid_set.user_scid, - &conn->dcid_set.current_dcid); + &conn->dcid_set.current_dcid, + XQC_APP_PATH_STATUS_AVAILABLE); if (conn->conn_initial_path == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_conn_create_path_inner fail|"); return -XQC_EMP_CREATE_PATH; @@ -592,7 +630,7 @@ xqc_conn_find_path_by_dcid(xqc_connection_t *conn, xqc_cid_t *dcid) xqc_path_ctx_t * xqc_conn_create_path_inner(xqc_connection_t *conn, - xqc_cid_t *scid, xqc_cid_t *dcid) + xqc_cid_t *scid, xqc_cid_t *dcid, xqc_app_path_status_t path_status) { xqc_int_t ret = XQC_ERROR; xqc_path_ctx_t *path = NULL; @@ -603,6 +641,8 @@ xqc_conn_create_path_inner(xqc_connection_t *conn, return NULL; } + path->app_path_status = path_status; + ret = xqc_path_init(path, conn); if (ret != XQC_OK) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_path_init error|%d|", ret); @@ -658,14 +698,6 @@ xqc_request_path_metrics_print(xqc_connection_t *conn, xqc_h3_stream_t *h3_strea stats->mp_standby_path_send_weight = 0.0; stats->mp_standby_path_recv_weight = 0.0; - if (!conn->enable_multipath) { - return; - } - - if (conn->active_path_count < 2) { - return; - } - int available_path_cnt = 0, standby_path_cnt = 0; uint64_t aggregate_send_bytes = 0, aggregate_recv_bytes = 0; @@ -682,6 +714,9 @@ xqc_request_path_metrics_print(xqc_connection_t *conn, xqc_h3_stream_t *h3_strea uint64_t send_bytes = h3_stream->paths_info[path->path_id].path_send_bytes; uint64_t recv_bytes = h3_stream->paths_info[path->path_id].path_recv_bytes; + h3_stream->paths_info[path->path_id].path_srtt = path->path_send_ctl->ctl_srtt; + h3_stream->paths_info[path->path_id].path_app_status = path->app_path_status; + if (send_bytes > 0 || recv_bytes > 0) { aggregate_send_bytes += send_bytes; aggregate_recv_bytes += recv_bytes; @@ -697,24 +732,29 @@ xqc_request_path_metrics_print(xqc_connection_t *conn, xqc_h3_stream_t *h3_strea } } - if ((available_path_cnt > 0) && (standby_path_cnt > 0)) { - stats->mp_state = 1; + if (conn->enable_multipath && conn->active_path_count >= 2) { + if ((available_path_cnt > 0) && (standby_path_cnt > 0)) { + stats->mp_state = 1; + + } else if ((available_path_cnt == 0) && (standby_path_cnt > 0)) { + stats->mp_state = 2; - } else if ((available_path_cnt == 0) && (standby_path_cnt > 0)) { - stats->mp_state = 2; + } else if ((available_path_cnt > 0) && (standby_path_cnt == 0)) { + stats->mp_state = 3; + } - } else if ((available_path_cnt > 0) && (standby_path_cnt == 0)) { - stats->mp_state = 3; + } else { + stats->mp_state = 0; } if (aggregate_send_bytes != 0) { - stats->mp_default_path_send_weight = (float)(h3_stream->paths_info[XQC_INITIAL_PATH_ID].path_send_bytes) / aggregate_send_bytes; stats->mp_standby_path_send_weight = (float)(standby_path_send_bytes) / aggregate_send_bytes; + stats->mp_default_path_send_weight = 1.0 - stats->mp_standby_path_send_weight; } if (aggregate_recv_bytes != 0) { - stats->mp_default_path_recv_weight = (float)(h3_stream->paths_info[XQC_INITIAL_PATH_ID].path_recv_bytes) / aggregate_recv_bytes; stats->mp_standby_path_recv_weight = (float)(standby_path_recv_bytes) / aggregate_recv_bytes; + stats->mp_default_path_recv_weight = 1.0 - stats->mp_standby_path_recv_weight; } } @@ -783,7 +823,9 @@ void xqc_stream_path_metrics_on_send(xqc_connection_t *conn, xqc_packet_out_t *po) { for (int i = 0; i < XQC_MAX_STREAM_FRAME_IN_PO; i++) { - if (po->po_stream_frames[i].ps_is_used == 1) { + if (po->po_stream_frames[i].ps_is_used == 1 + && po->po_stream_frames[i].ps_is_reset == 0) + { xqc_stream_t * stream = xqc_find_stream_by_id(po->po_stream_frames[i].ps_stream_id, conn->streams_hash); if (stream != NULL && po->po_path_id < XQC_MAX_PATHS_COUNT) { @@ -795,6 +837,11 @@ xqc_stream_path_metrics_on_send(xqc_connection_t *conn, xqc_packet_out_t *po) stream->paths_info[po->po_path_id].path_send_reinject_bytes += po->po_used_size; } } + + conn->stream_stats.send_bytes += po->po_stream_frames[i].ps_length; + if (po->po_flag & XQC_POF_REINJECTED_REPLICA) { + conn->stream_stats.reinjected_bytes += po->po_stream_frames[i].ps_length; + } } } } @@ -827,7 +874,7 @@ xqc_path_metrics_print(xqc_connection_t *conn, char *buff, unsigned buff_size) // TODO 关于数据抓取说明 ret = snprintf(buff + cursor, buff_size - cursor, "#%"PRIu64"-%d-%d-%"PRIu64"-%.4f-%"PRIu32"-%"PRIu32"-%"PRIu32"-%"PRIu32"-%"PRIu32"" - "-%"PRIu32"-%"PRIu32"-%"PRIu32"-%"PRIu64"-%"PRIu64"-%"PRIu32"-%"PRIu32"-%"PRIu32, + "-%"PRIu32"-%"PRIu32"-%"PRIu32"-%"PRIu64"-%"PRIu64"-%"PRIu32"-%"PRIu32, path->path_id, path->path_state, path->app_path_status, @@ -844,42 +891,13 @@ xqc_path_metrics_print(xqc_connection_t *conn, char *buff, unsigned buff_size) path->path_send_ctl->ctl_bytes_send, path->path_send_ctl->ctl_bytes_recv, path->standby_probe_count, - path->app_path_status_changed_count, - path->tra_path_status_changed_count); + path->app_path_status_changed_count); cursor += ret; } } } -void -xqc_h3s_path_metrics_print(xqc_h3_stream_t *h3_stream, char *buff, unsigned buff_size) -{ - int cursor = 0; - int ret = 0; - - for (int i = 0; i < XQC_MAX_PATHS_COUNT; ++i) { - - if (h3_stream->paths_info[i].path_id == XQC_MAX_UINT64_VALUE) { - continue; - } - - if (cursor >= (buff_size - 100)) { // enough space - break; - } - - ret = snprintf(buff + cursor, buff_size - cursor, - "#%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64"-%"PRIu64, - h3_stream->paths_info[i].path_id, - h3_stream->paths_info[i].path_pkt_send_count, - h3_stream->paths_info[i].path_pkt_recv_count, - h3_stream->paths_info[i].path_send_bytes, - h3_stream->paths_info[i].path_send_reinject_bytes); - - cursor += ret; - } -} - void xqc_path_send_buffer_append(xqc_path_ctx_t *path, xqc_packet_out_t *packet_out, xqc_list_head_t *head) @@ -1033,6 +1051,7 @@ xqc_conn_server_init_path_addr(xqc_connection_t *conn, uint64_t path_id, ret = xqc_memcpy_with_cap(path->local_addr, sizeof(path->local_addr), local_addr, local_addrlen); if (ret == XQC_OK) { path->local_addrlen = local_addrlen; + path->addr_str_len = 0; } else { xqc_log(conn->log, XQC_LOG_ERROR, @@ -1046,6 +1065,7 @@ xqc_conn_server_init_path_addr(xqc_connection_t *conn, uint64_t path_id, ret = xqc_memcpy_with_cap(path->peer_addr, sizeof(path->peer_addr), peer_addr, peer_addrlen); if (ret == XQC_OK) { path->peer_addrlen = peer_addrlen; + path->addr_str_len = 0; } else { xqc_log(conn->log, XQC_LOG_ERROR, @@ -1219,7 +1239,6 @@ xqc_path_record_info(xqc_path_ctx_t *path, xqc_path_info_t *path_info) path_info->path_id = path->path_id; path_info->path_state = (uint8_t)path->path_state; path_info->app_path_status = (uint8_t)path->app_path_status; - path_info->tra_path_status = (uint8_t)path->tra_path_status; path_info->path_bytes_send = path->path_send_ctl->ctl_bytes_send; path_info->path_bytes_recv = path->path_send_ctl->ctl_bytes_recv; @@ -1230,7 +1249,6 @@ xqc_path_record_info(xqc_path_ctx_t *path, xqc_path_info_t *path_info) path_info->standby_probe_count = path->standby_probe_count; path_info->app_path_status_changed_count = path->app_path_status_changed_count; - path_info->tra_path_status_changed_count = path->tra_path_status_changed_count; path_info->pkt_recv_cnt = path->path_send_ctl->ctl_recv_count; path_info->pkt_send_cnt = path->path_send_ctl->ctl_send_count; @@ -1275,7 +1293,6 @@ xqc_set_application_path_status(xqc_path_ctx_t *path, xqc_app_path_status_t stat xqc_log(conn->log, XQC_LOG_DEBUG, "|path:%ui|app_path_status:%d->%d|", path->path_id, last_status, status); path->app_path_status_changed_count++; path->last_app_path_status_changed_time = xqc_monotonic_timestamp(); - xqc_set_transport_path_status(path, (xqc_tra_path_status_t)status, path->last_app_path_status_changed_time); if (status == XQC_APP_PATH_STATUS_FROZEN) { xqc_path_move_unack_packets_from_conn(path, conn); @@ -1288,43 +1305,6 @@ xqc_set_application_path_status(xqc_path_ctx_t *path, xqc_app_path_status_t stat return XQC_OK; } -void xqc_set_transport_path_status(xqc_path_ctx_t *path, xqc_tra_path_status_t status, xqc_usec_t now) -{ - if (path->tra_path_status == status - || status <= XQC_TRA_PATH_STATUS_NONE - || status >= XQC_TRA_PATH_STATUS_MAX) - { - return; - } - - xqc_connection_t *conn = path->parent_conn; - - if (path->path_state == XQC_PATH_STATE_ACTIVE) { - - if ((path->tra_path_status == XQC_TRA_PATH_STATUS_IN_USE) - && (status != XQC_TRA_PATH_STATUS_IN_USE)) - { - conn->in_use_active_path_count--; - - } else if ((path->tra_path_status != XQC_TRA_PATH_STATUS_IN_USE) - && (status == XQC_TRA_PATH_STATUS_IN_USE)) - { - conn->in_use_active_path_count++; - } - - } - - xqc_log(conn->log, XQC_LOG_DEBUG, "|path:%ui|tra_path_status:%d->%d|", path->path_id, path->tra_path_status, status); - path->tra_path_status = status; - - path->tra_path_status_changed_count++; - path->last_tra_path_status_changed_time = now; - - xqc_send_ctl_t *send_ctl = path->path_send_ctl; - send_ctl->ctl_pto_count_since_last_tra_path_status_changed = 0; - send_ctl->ctl_send_count_at_last_tra_path_status_changed_time = send_ctl->ctl_send_count; -} - xqc_int_t xqc_conn_mark_path_standby(xqc_engine_t *engine, const xqc_cid_t *cid, uint64_t path_id) @@ -1471,35 +1451,7 @@ xqc_path_standby_probe(xqc_path_ctx_t *path) return ret; } + xqc_log(conn->log, XQC_LOG_DEBUG, "|PING|path:%ui|", path->path_id); path->standby_probe_count++; return XQC_OK; -} - -xqc_path_stats_t -xqc_path_get_stats(xqc_engine_t *engine, const xqc_cid_t *cid, uint64_t path_id) -{ - xqc_connection_t *conn; - xqc_path_ctx_t *path; - xqc_path_stats_t path_stats; - xqc_memzero(&path_stats, sizeof(path_stats)); - conn = xqc_engine_conns_hash_find(engine, cid, 's'); - if (!conn) { - xqc_log(engine->log, XQC_LOG_ERROR, "|can not find connection|"); - return path_stats; - } - if (conn->conn_state >= XQC_CONN_STATE_CLOSING) { - return path_stats; - } - 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|"); - return path_stats; - } - xqc_send_ctl_t *send_ctl = path->path_send_ctl; - path_stats.last_tra_path_status_changed_time = path->last_tra_path_status_changed_time; - path_stats.send_count_since_last_tra_path_status_changed = - send_ctl->ctl_send_count - send_ctl->ctl_send_count_at_last_tra_path_status_changed_time; - path_stats.pto_count_since_last_tra_path_status_changed = send_ctl->ctl_pto_count_since_last_tra_path_status_changed; - path_stats.get_stats_success = 1; - return path_stats; -} +} \ No newline at end of file diff --git a/src/transport/xqc_multipath.h b/src/transport/xqc_multipath.h index 90f6eb203..7ddb220bb 100644 --- a/src/transport/xqc_multipath.h +++ b/src/transport/xqc_multipath.h @@ -44,13 +44,6 @@ typedef enum { XQC_APP_PATH_STATUS_MAX, } xqc_app_path_status_t; -/* transport layer path status */ -typedef enum { - XQC_TRA_PATH_STATUS_NONE, - XQC_TRA_PATH_STATUS_BACKUP = 1, - XQC_TRA_PATH_STATUS_IN_USE = 2, - XQC_TRA_PATH_STATUS_MAX, -} xqc_tra_path_status_t; /* path close mode: passive & proactive */ typedef enum { @@ -69,6 +62,7 @@ typedef enum { typedef enum { XQC_PATH_FLAG_SEND_STATUS = 1 << 0, XQC_PATH_FLAG_RECV_STATUS = 1 << 1, + XQC_PATH_FLAG_SOCKET_ERROR = 1 << 2, } xqc_path_flag_t; typedef enum { @@ -88,6 +82,7 @@ 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_scid; /* Path_address: 4-tuple */ unsigned char peer_addr[sizeof(struct sockaddr_in6)], @@ -113,8 +108,6 @@ struct xqc_path_ctx_s { xqc_path_flag_t path_flag; - /* transport layer path status */ - xqc_tra_path_status_t tra_path_status; /* application layer path status, sync via PATH_STATUS frame */ xqc_app_path_status_t app_path_status; xqc_app_path_status_t next_app_path_state; @@ -122,7 +115,6 @@ struct xqc_path_ctx_s { uint64_t app_path_status_recv_seq_num; xqc_usec_t last_app_path_status_changed_time; - xqc_usec_t last_tra_path_status_changed_time; /* Path cc & ack tracking */ xqc_send_ctl_t *path_send_ctl; @@ -145,7 +137,6 @@ struct xqc_path_ctx_s { /* path backup mode */ uint32_t standby_probe_count; uint32_t app_path_status_changed_count; - uint32_t tra_path_status_changed_count; /* PTMUD */ size_t curr_pkt_out_size; @@ -158,7 +149,6 @@ typedef struct { uint64_t path_id; uint8_t path_state; uint8_t app_path_status; - uint8_t tra_path_status; uint64_t path_bytes_send; uint64_t path_bytes_recv; @@ -178,7 +168,6 @@ typedef struct { uint32_t standby_probe_count; uint32_t app_path_status_changed_count; - uint32_t tra_path_status_changed_count; } xqc_path_info_t; @@ -190,6 +179,9 @@ xqc_int_t xqc_generate_path_challenge_data(xqc_connection_t *conn, xqc_path_ctx_ /* check mp support */ xqc_multipath_mode_t xqc_conn_enable_multipath(xqc_connection_t *conn); +/* check multipath version negotiation */ +xqc_multipath_version_t xqc_conn_multipath_version_negotiation(xqc_connection_t *conn); + /* init path_list & initial path for connection */ xqc_int_t xqc_conn_init_paths_list(xqc_connection_t *conn); @@ -200,7 +192,8 @@ void xqc_path_schedule_buf_destroy(xqc_path_ctx_t *path); void xqc_path_schedule_buf_pre_destroy(xqc_send_queue_t *send_queue, xqc_path_ctx_t *path); /* create path inner */ -xqc_path_ctx_t *xqc_conn_create_path_inner(xqc_connection_t *conn, xqc_cid_t *scid, xqc_cid_t *dcid); +xqc_path_ctx_t *xqc_conn_create_path_inner(xqc_connection_t *conn, + xqc_cid_t *scid, xqc_cid_t *dcid, xqc_app_path_status_t path_status); /* server update client addr when recv path_challenge frame */ xqc_int_t xqc_conn_server_init_path_addr(xqc_connection_t *conn, uint64_t path_id, @@ -228,7 +221,6 @@ void xqc_path_send_buffer_remove(xqc_path_ctx_t *path, xqc_packet_out_t *packet_ void xqc_path_send_buffer_clear(xqc_connection_t *conn, xqc_path_ctx_t *path, xqc_list_head_t *head, xqc_send_type_t send_type); xqc_int_t xqc_set_application_path_status(xqc_path_ctx_t *path, xqc_app_path_status_t status, xqc_bool_t is_tx); -void xqc_set_transport_path_status(xqc_path_ctx_t *path, xqc_tra_path_status_t status, xqc_usec_t now); /* path statistics */ void xqc_conn_path_metrics_print(xqc_connection_t *conn, xqc_conn_stats_t *stats); @@ -239,12 +231,13 @@ void xqc_stream_path_metrics_on_send(xqc_connection_t *conn, xqc_packet_out_t *p void xqc_stream_path_metrics_on_recv(xqc_connection_t *conn, xqc_stream_t *stream, xqc_packet_in_t *pi); void xqc_path_metrics_print(xqc_connection_t *conn, char *buff, unsigned buff_size); -void xqc_h3s_path_metrics_print(xqc_h3_stream_t *h3_stream, char *buff, unsigned buff_size); xqc_msec_t xqc_path_get_idle_timeout(xqc_path_ctx_t *path); void xqc_path_validate(xqc_path_ctx_t *path); +xqc_int_t xqc_conn_is_current_mp_version_supported(xqc_multipath_version_t mp_version); + xqc_bool_t xqc_path_is_initial_path(xqc_path_ctx_t *path); void xqc_path_record_info(xqc_path_ctx_t *path, xqc_path_info_t *path_info); diff --git a/src/transport/xqc_packet_out.c b/src/transport/xqc_packet_out.c index 1fa4a4176..433428e2a 100644 --- a/src/transport/xqc_packet_out.c +++ b/src/transport/xqc_packet_out.c @@ -418,92 +418,139 @@ xqc_write_ack_to_one_packet(xqc_connection_t *conn, xqc_packet_out_t *packet_out return ret; } -int -xqc_write_ack_to_packets(xqc_connection_t *conn) +xqc_int_t +xqc_write_ack_or_mp_ack_to_one_packet(xqc_connection_t *conn, xqc_packet_out_t *packet_out, xqc_pkt_num_space_t pns, xqc_path_ctx_t *path, xqc_bool_t is_mp_ack) +{ + int ret; + if (is_mp_ack) { + ret = xqc_write_ack_mp_to_one_packet(conn, path, packet_out, pns); + } else { + ret = xqc_write_ack_to_one_packet(conn, packet_out, pns); + } + return ret; +} + +xqc_int_t +xqc_write_ack_or_mp_ack_to_packets(xqc_connection_t *conn) { XQC_DEBUG_PRINT xqc_pkt_num_space_t pns; xqc_packet_out_t *packet_out; xqc_pkt_type_t pkt_type; xqc_list_head_t *pos, *next; + xqc_bool_t is_mp_ack = 0; /* Send ack in default case */ int ret; - + + xqc_path_ctx_t *path; + xqc_list_head_t *path_pos, *path_next; + for (pns = 0; pns < XQC_PNS_N; ++pns) { + + /* If there's no such pns in the connection, continue the loop */ if (!(conn->conn_flag & (XQC_CONN_FLAG_SHOULD_ACK_INIT << pns))) { continue; } - if (pns == XQC_PNS_HSK) { - pkt_type = XQC_PTYPE_HSK; + 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; + } - } else if (pns == XQC_PNS_INIT) { - pkt_type = XQC_PTYPE_INIT; + /* Check if any ack should be sent in current path */ + xqc_pn_ctl_t *pn_ctl = xqc_get_pn_ctl(conn, path); + xqc_pktno_range_node_t *first_range = NULL; + xqc_list_head_t *tmp_pos, *tmp_next; + xqc_list_for_each_safe(tmp_pos, tmp_next, &pn_ctl->ctl_recv_record[pns].list_head) { + first_range = xqc_list_entry(tmp_pos, xqc_pktno_range_node_t, list); + break; + } + if (first_range == NULL) { + continue; + } - } else { - pkt_type = XQC_PTYPE_SHORT_HEADER; - } + if (pns == XQC_PNS_HSK) { + pkt_type = XQC_PTYPE_HSK; + } else if (pns == XQC_PNS_INIT) { + pkt_type = XQC_PTYPE_INIT; + } else { + pkt_type = XQC_PTYPE_SHORT_HEADER; + } + /* Acknowledgements of Initial and Handshake packets MUST be carried using ACK frames */ + if (pkt_type > XQC_PTYPE_HSK && conn->enable_multipath == XQC_CONN_MULTIPATH_MULTIPLE_PNS) { + is_mp_ack = 1; + } + path_buffer: - xqc_list_for_each_safe(pos, next, &conn->conn_initial_path->path_schedule_buf[XQC_SEND_TYPE_NORMAL]) { - packet_out = xqc_list_entry(pos, xqc_packet_out_t, po_list); - if (xqc_packet_out_can_attach_ack(packet_out, conn->conn_initial_path, pkt_type)) { - ret = xqc_write_ack_to_one_packet(conn, packet_out, pns); - if (ret == -XQC_ENOBUF) { - xqc_log(conn->log, XQC_LOG_DEBUG, "|xqc_write_ack_to_one_packet try conn buffer|"); - goto write_new; - - } else if (ret == XQC_OK) { - goto done; - - } else { - return ret; + /* Try to attach ack or mp_ack to packet_out in path_buffer */ + xqc_list_for_each_safe(pos, next, &path->path_schedule_buf[XQC_SEND_TYPE_NORMAL]) { + packet_out = xqc_list_entry(pos, xqc_packet_out_t, po_list); + + if (xqc_packet_out_can_attach_ack(packet_out, path, pkt_type)) { + ret = xqc_write_ack_or_mp_ack_to_one_packet(conn, packet_out, pns, path, is_mp_ack); + if (ret == -XQC_ENOBUF) { + xqc_log(conn->log, XQC_LOG_DEBUG, "|xqc_write_ack_or_mp_ack_to_one_packet try new packet|"); + goto write_new; + } else if (ret == XQC_OK) { + goto done; + } else { + return ret; + } } - } - goto write_new; - } + goto write_new; + } conn_buffer: - 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 (xqc_packet_out_can_attach_ack(packet_out, conn->conn_initial_path, pkt_type)) { - ret = xqc_write_ack_to_one_packet(conn, packet_out, pns); - if (ret == -XQC_ENOBUF) { - xqc_log(conn->log, XQC_LOG_DEBUG, "|xqc_write_ack_to_one_packet try new packet|"); + if (!is_mp_ack) { + 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 (xqc_packet_out_can_attach_ack(packet_out, path, pkt_type)) { + ret = xqc_write_ack_to_one_packet(conn, packet_out, pns); + if (ret == -XQC_ENOBUF) { + xqc_log(conn->log, XQC_LOG_DEBUG, "|xqc_write_ack_to_one_packet try new packet|"); + goto write_new; + } else if (ret == XQC_OK) { + goto done; + } else { + return ret; + } + } goto write_new; - - } else if (ret == XQC_OK) { - goto done; - - } else { - return ret; } } - goto write_new; - } write_new: - packet_out = xqc_write_new_packet(conn, pkt_type); - if (packet_out == NULL) { - xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); - return -XQC_EWRITE_PKT; - } - - ret = xqc_write_ack_to_one_packet(conn, packet_out, pns); - if (ret != XQC_OK) { - xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_ack_to_one_packet error|ret:%d|", ret); - return ret; - } + packet_out = xqc_write_new_packet(conn, pkt_type); + if (packet_out == NULL) { + xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); + return -XQC_EWRITE_PKT; + } + ret = xqc_write_ack_or_mp_ack_to_one_packet(conn, packet_out, pns, path, is_mp_ack); + if (ret != XQC_OK) { + xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_ack_or_mp_ack_to_one_packet write to new packet error|ret:%d|", ret); + return ret; + } pure_ack: - /* send ack packet first */ - xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); + /* send ack packet first */ + xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); + done: - xqc_log(conn->log, XQC_LOG_DEBUG, "|pns:%d|", pns); + xqc_log(conn->log, XQC_LOG_DEBUG, "|path:%ui|pns:%d|", path->path_id, pns); + + /* Ack frame should only be sent on initial path */ + if (!is_mp_ack) { + break; + } + } } return XQC_OK; } + int xqc_write_ping_to_packet(xqc_connection_t *conn, xqc_path_ctx_t *path, void *po_user_data, xqc_bool_t notify, xqc_ping_record_t *pr) @@ -691,7 +738,7 @@ xqc_write_stop_sending_to_packet(xqc_connection_t *conn, xqc_stream_t *stream, return XQC_OK; } - packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); + packet_out = xqc_write_new_packet(conn, XQC_PTYPE_NUM); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); return -XQC_EWRITE_PKT; @@ -830,12 +877,12 @@ xqc_write_max_data_to_packet(xqc_connection_t *conn, uint64_t max_data) } int -xqc_write_max_stream_data_to_packet(xqc_connection_t *conn, xqc_stream_id_t stream_id, uint64_t max_stream_data) +xqc_write_max_stream_data_to_packet(xqc_connection_t *conn, xqc_stream_id_t stream_id, uint64_t max_stream_data, xqc_pkt_type_t pkt_type) { ssize_t ret = XQC_OK; xqc_packet_out_t *packet_out; - packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); + packet_out = xqc_write_new_packet(conn, pkt_type); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); return -XQC_EWRITE_PKT; @@ -932,10 +979,57 @@ xqc_write_new_token_to_packet(xqc_connection_t *conn) int xqc_write_stream_frame_to_packet(xqc_connection_t *conn, xqc_stream_t *stream, xqc_pkt_type_t pkt_type, uint8_t fin, - const unsigned char *payload, size_t payload_size, size_t *send_data_written) + const unsigned char *payload, size_t payload_size, + size_t *send_data_written) { xqc_packet_out_t *packet_out; int n_written; + + /* increase recv window */ + xqc_usec_t max_srtt = 0; + uint64_t old_fc_win = 0; + uint64_t available_window; + + if (conn->conn_settings.enable_stream_rate_limit + && stream->stream_send_offset == 0 + && stream->stream_type == XQC_CLI_BID) + { + available_window = stream->stream_flow_ctl.fc_max_stream_data_can_recv - stream->stream_data_in.next_read_offset; + old_fc_win = stream->stream_flow_ctl.fc_stream_recv_window_size; + + if (stream->recv_rate_bytes_per_sec) { + /* set window according to the rate limit */ + max_srtt = xqc_conn_get_max_srtt(conn); + stream->stream_flow_ctl.fc_stream_recv_window_size = stream->recv_rate_bytes_per_sec * max_srtt / 1000000; + stream->stream_flow_ctl.fc_stream_recv_window_size = xqc_max(conn->conn_settings.init_recv_window, stream->stream_flow_ctl.fc_stream_recv_window_size); + stream->stream_flow_ctl.fc_stream_recv_window_size = xqc_min(XQC_MAX_RECV_WINDOW, stream->stream_flow_ctl.fc_stream_recv_window_size); + xqc_log(conn->log, XQC_LOG_DEBUG, + "|initial_fc_credit_update|stream:%ui|rate:%ui|srtt:%ui|old_fc_win:%ui|fc_win:%ui|", + stream->stream_id, + stream->recv_rate_bytes_per_sec, max_srtt, + old_fc_win, stream->stream_flow_ctl.fc_stream_recv_window_size); + + } else { + /* set window to XQC_MAX_RECV_WINDOW */ + stream->stream_flow_ctl.fc_stream_recv_window_size = XQC_MAX_RECV_WINDOW; + xqc_log(conn->log, XQC_LOG_DEBUG, + "|initial_fc_credit_update|stream:%ui|no_rate_limit|old_fc_win:%ui|fc_win:%ui|", + stream->stream_id, + old_fc_win, stream->stream_flow_ctl.fc_stream_recv_window_size); + } + + if (stream->stream_flow_ctl.fc_stream_recv_window_size > available_window) { + stream->stream_flow_ctl.fc_max_stream_data_can_recv += (stream->stream_flow_ctl.fc_stream_recv_window_size - available_window); + xqc_log(conn->log, XQC_LOG_DEBUG, + "|initial_fc_credit_update|stream:%ui|new_max_data:%ui|stream_max_recv_offset:%ui|next_read_offset:%ui|window_size:%ui|pkt_type:%d|", + stream->stream_id, + stream->stream_flow_ctl.fc_max_stream_data_can_recv, stream->stream_max_recv_offset, + stream->stream_data_in.next_read_offset, stream->stream_flow_ctl.fc_stream_recv_window_size, + pkt_type); + xqc_write_max_stream_data_to_packet(conn, stream->stream_id, stream->stream_flow_ctl.fc_max_stream_data_can_recv, pkt_type); + } + } + /* We need 25 bytes for stream frame header at most, and left bytes for stream data. * It's a trade-off value, bigger need bytes for higher payload rate. */ const unsigned need = 50; @@ -959,13 +1053,6 @@ xqc_write_stream_frame_to_packet(xqc_connection_t *conn, packet_out->po_stream_id = stream->stream_id; packet_out->po_stream_offset = stream->stream_send_offset; - if (stream->stream_mp_usage_schedule == 0) { - packet_out->po_flag |= XQC_POF_NOT_SCHEDULE; - } - if (stream->stream_mp_usage_reinject == 0) { - packet_out->po_flag |= XQC_POF_NOT_REINJECT; - } - if (fin && *send_data_written == payload_size) { stream->stream_flag |= XQC_STREAM_FLAG_FIN_WRITE; stream->stream_stats.local_fin_write_time = xqc_monotonic_timestamp(); @@ -1156,7 +1243,8 @@ xqc_write_retire_conn_id_frame_to_packet(xqc_connection_t *conn, uint64_t seq_nu xqc_int_t -xqc_write_path_challenge_frame_to_packet(xqc_connection_t *conn, xqc_path_ctx_t *path) +xqc_write_path_challenge_frame_to_packet(xqc_connection_t *conn, + xqc_path_ctx_t *path, xqc_bool_t attach_path_status) { xqc_int_t ret = XQC_ERROR; @@ -1177,6 +1265,24 @@ xqc_write_path_challenge_frame_to_packet(xqc_connection_t *conn, xqc_path_ctx_t packet_out->po_path_flag |= XQC_PATH_SPECIFIED_BY_PCPR; packet_out->po_path_id = path->path_id; + if (attach_path_status) { + path->app_path_status_send_seq_num++; + //TODO: MPQUIC fix migration + ret = xqc_gen_path_status_frame(conn, packet_out, path->path_scid.cid_seq_num, + path->app_path_status_send_seq_num, (uint64_t)path->app_path_status); + if (ret < 0) { + /* ignore */ + xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_path_status_frame error|%d|", ret); + + } else { + xqc_log(conn->log, XQC_LOG_DEBUG, + "|initial_path_status|status:%d|frames:%s|", + path->app_path_status, + xqc_frame_type_2_str(packet_out->po_frame_types)); + 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, "|path:%ui|", path->path_id); @@ -1222,96 +1328,6 @@ xqc_write_path_response_frame_to_packet(xqc_connection_t *conn, xqc_path_ctx_t * } -int -xqc_write_ack_mp_to_packets(xqc_connection_t *conn) -{ - XQC_DEBUG_PRINT - xqc_pkt_num_space_t pns; - xqc_packet_out_t *packet_out; - xqc_pkt_type_t pkt_type; - xqc_list_head_t *pos, *next; - - int ret; - - xqc_path_ctx_t *path; - xqc_list_head_t *path_pos, *path_next; - - for (pns = 0; pns < XQC_PNS_N; ++pns) { - if (!(conn->conn_flag & (XQC_CONN_FLAG_SHOULD_ACK_INIT << pns))) { - continue; - } - - 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; - } - xqc_pn_ctl_t *pn_ctl = xqc_get_pn_ctl(conn, path); - - xqc_pktno_range_node_t *first_range = NULL; - xqc_list_head_t *tmp_pos, *tmp_next; - xqc_list_for_each_safe(tmp_pos, tmp_next, &pn_ctl->ctl_recv_record[pns].list_head) { - first_range = xqc_list_entry(tmp_pos, xqc_pktno_range_node_t, list); - break; - } - if (first_range == NULL) { - continue; - } - - if (pns == XQC_PNS_HSK) { - pkt_type = XQC_PTYPE_HSK; - - } else if (pns == XQC_PNS_INIT) { - pkt_type = XQC_PTYPE_INIT; - - } else { - pkt_type = XQC_PTYPE_SHORT_HEADER; - } - -path_buffer: - xqc_list_for_each_safe(pos, next, &path->path_schedule_buf[XQC_SEND_TYPE_NORMAL]) { - packet_out = xqc_list_entry(pos, xqc_packet_out_t, po_list); - if (xqc_packet_out_can_attach_ack(packet_out, path, pkt_type)) { - ret = xqc_write_ack_mp_to_one_packet(conn, path, packet_out, pns); - if (ret == -XQC_ENOBUF) { - xqc_log(conn->log, XQC_LOG_DEBUG, "|xqc_write_ack_mp_to_one_packet try new packet|"); - goto write_new; - - } else if (ret == XQC_OK) { - goto done; - - } else { - return ret; - } - } - goto write_new; - } - -write_new: - /* 指定 MP_ACK 从原路径发送 */ - packet_out = xqc_write_new_packet(conn, pkt_type); - if (packet_out == NULL) { - xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); - return -XQC_EWRITE_PKT; - } - - ret = xqc_write_ack_mp_to_one_packet(conn, path, packet_out, pns); - if (ret != XQC_OK) { - xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_ack_mp_to_one_packet error|ret:%d|", ret); - return ret; - } -pure_ack: - /* send ack packet first */ - xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); -done: - xqc_log(conn->log, XQC_LOG_DEBUG, "|path:%ui|pns:%d|", path->path_id, pns); - - } - } - return XQC_OK; -} - int xqc_write_ack_mp_to_one_packet(xqc_connection_t *conn, xqc_path_ctx_t *path, xqc_packet_out_t *packet_out, xqc_pkt_num_space_t pns) @@ -1323,7 +1339,8 @@ xqc_write_ack_mp_to_one_packet(xqc_connection_t *conn, xqc_path_ctx_t *path, xqc_pn_ctl_t *pn_ctl = xqc_get_pn_ctl(conn, path); - ret = xqc_gen_ack_mp_frame(conn, path->path_id, packet_out, now, + //TODO: MPQUIC fix migration + ret = xqc_gen_ack_mp_frame(conn, path->path_scid.cid_seq_num, packet_out, now, conn->local_settings.ack_delay_exponent, &pn_ctl->ctl_recv_record[packet_out->po_pkt.pkt_pns], path->path_send_ctl->ctl_largest_recv_time[pns], @@ -1369,9 +1386,10 @@ xqc_write_path_abandon_frame_to_packet(xqc_connection_t *conn, xqc_path_ctx_t *p } /* dcid_seq_num = path->scid.cid_seq_num */ - uint64_t dcid_seq_num = path->path_id; + //TODO: MPQUIC fix migration + uint64_t dcid_seq_num = path->path_scid.cid_seq_num; - ret = xqc_gen_path_abandon_frame(packet_out, dcid_seq_num, 0); + ret = xqc_gen_path_abandon_frame(conn, packet_out, dcid_seq_num, 0); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_path_abandon_frame error|%d|", ret); goto error; @@ -1403,7 +1421,8 @@ xqc_write_path_status_frame_to_packet(xqc_connection_t *conn, xqc_path_ctx_t *pa } path->app_path_status_send_seq_num++; - ret = xqc_gen_path_status_frame(packet_out, path->path_id, + //TODO: MPQUIC fix migration + ret = xqc_gen_path_status_frame(conn, packet_out, path->path_scid.cid_seq_num, path->app_path_status_send_seq_num, (uint64_t)path->app_path_status); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_path_status_frame error|%d|", ret); diff --git a/src/transport/xqc_packet_out.h b/src/transport/xqc_packet_out.h index 89b9b2074..b57baa499 100644 --- a/src/transport/xqc_packet_out.h +++ b/src/transport/xqc_packet_out.h @@ -146,7 +146,10 @@ xqc_packet_out_t *xqc_write_packet_for_stream(xqc_connection_t *conn, xqc_pkt_ty int xqc_write_packet_header(xqc_connection_t *conn, xqc_packet_out_t *packet_out); -int xqc_write_ack_to_packets(xqc_connection_t *conn); +xqc_int_t xqc_write_ack_or_mp_ack_to_packets(xqc_connection_t *conn); + +xqc_int_t xqc_write_ack_or_mp_ack_to_one_packet(xqc_connection_t *conn, xqc_packet_out_t *packet_out, + xqc_pkt_num_space_t pns, xqc_path_ctx_t *path, xqc_bool_t is_mp_ack); int xqc_write_ack_to_one_packet(xqc_connection_t *conn, xqc_packet_out_t *packet_out, xqc_pkt_num_space_t pns); @@ -167,7 +170,8 @@ int xqc_write_streams_blocked_to_packet(xqc_connection_t *conn, uint64_t stream_ int xqc_write_max_data_to_packet(xqc_connection_t *conn, uint64_t max_data); -int xqc_write_max_stream_data_to_packet(xqc_connection_t *conn, xqc_stream_id_t stream_id, uint64_t max_stream_data); +int xqc_write_max_stream_data_to_packet(xqc_connection_t *conn, +xqc_stream_id_t stream_id, uint64_t max_stream_data, xqc_pkt_type_t xqc_pkt_type); int xqc_write_max_streams_to_packet(xqc_connection_t *conn, uint64_t max_stream, int bidirectional); @@ -186,13 +190,12 @@ xqc_int_t xqc_write_new_conn_id_frame_to_packet(xqc_connection_t *conn, uint64_t xqc_int_t xqc_write_retire_conn_id_frame_to_packet(xqc_connection_t *conn, uint64_t seq_num); -xqc_int_t xqc_write_path_challenge_frame_to_packet(xqc_connection_t *conn, xqc_path_ctx_t *path); +xqc_int_t xqc_write_path_challenge_frame_to_packet(xqc_connection_t *conn, xqc_path_ctx_t *path, + xqc_bool_t attach_path_status); xqc_int_t xqc_write_path_response_frame_to_packet(xqc_connection_t *conn, xqc_path_ctx_t *path, unsigned char *path_response_data); -int xqc_write_ack_mp_to_packets(xqc_connection_t *conn); - int xqc_write_ack_mp_to_one_packet(xqc_connection_t *conn, xqc_path_ctx_t *path, xqc_packet_out_t *packet_out, xqc_pkt_num_space_t pns); diff --git a/src/transport/xqc_packet_parser.c b/src/transport/xqc_packet_parser.c index 39e6f548c..a55ed8e28 100644 --- a/src/transport/xqc_packet_parser.c +++ b/src/transport/xqc_packet_parser.c @@ -241,6 +241,7 @@ xqc_packet_parse_short_header(xqc_connection_t *c, xqc_packet_in_t *packet_in) unsigned char *pos = packet_in->pos; unsigned char *end = packet_in->last; xqc_packet_t *packet = &packet_in->pi_pkt; + xqc_path_ctx_t *path = NULL; uint8_t cid_len = c->scid_set.user_scid.cid_len; packet_in->pi_pkt.pkt_type = XQC_PTYPE_SHORT_HEADER; @@ -271,7 +272,43 @@ xqc_packet_parse_short_header(xqc_connection_t *c, xqc_packet_in_t *packet_in) return -XQC_EILLPKT; } - packet_in->pi_path_id = packet->pkt_dcid.cid_seq_num; + //TODO: MPQUIC fix migration + if (c->enable_multipath) { + /* try to find the path */ + path = xqc_conn_find_path_by_scid(c, &packet_in->pi_pkt.pkt_dcid); +#ifndef XQC_NO_PID_PACKET_PROCESS + /* Note: to handle the case in which the server changes CID upon NAT rebinding */ + if (path == NULL && packet_in->pi_path_id != XQC_UNKNOWN_PATH_ID) { + path = xqc_conn_find_path_by_path_id(c, packet_in->pi_path_id); + + if (path == NULL) { + xqc_log(c->log, XQC_LOG_ERROR, "|can not find path|dcid:%s|path_id:%ui|", + xqc_dcid_str(&packet_in->pi_pkt.pkt_dcid), packet_in->pi_path_id); + return -XQC_EILLPKT; + } + + /* update the cid to a newer one */ + if (xqc_cid_is_equal(&path->path_last_scid, &packet_in->pi_pkt.pkt_dcid) != XQC_OK) { + xqc_cid_copy(&path->path_last_scid, &path->path_scid); + xqc_cid_copy(&path->path_scid, &packet_in->pi_pkt.pkt_dcid); + xqc_log(c->log, XQC_LOG_DEBUG, "|update_path_scid|%s->%s|", + xqc_scid_str(&path->path_last_scid), + xqc_dcid_str(&path->path_scid)); + } + } +#endif + + } else { + path = c->conn_initial_path; + } + + if (path != NULL) { + packet_in->pi_path_id = path->path_id; + + } else { + packet_in->pi_path_id = XQC_UNKNOWN_PATH_ID; + } + xqc_log(c->log, XQC_LOG_DEBUG, "|parse short header|path:%ui|pkt_dcid:%s|spin_bit:%ud|", packet_in->pi_path_id, xqc_scid_str(&(packet->pkt_dcid)), spin_bit); @@ -621,6 +658,7 @@ xqc_packet_encrypt_buf(xqc_connection_t *conn, xqc_packet_out_t *packet_out, } /* do packet protection */ + //TODO: MPQUIC fix migration uint32_t nonce_path_id = (conn->enable_multipath) ? (uint32_t)path->path_dcid.cid_seq_num : 0; ret = xqc_tls_encrypt_payload(conn->tls, level, @@ -741,17 +779,26 @@ xqc_packet_decrypt(xqc_connection_t *conn, xqc_packet_in_t *packet_in) xqc_packet_parse_packet_number(pktno, pktno_len, &truncated_pn); /* decode packet number */ - xqc_path_ctx_t *path = xqc_conn_find_path_by_path_id(conn, packet_in->pi_path_id); - if (path == NULL) { - path = conn->conn_initial_path; + // TODO: MPQUIC fix migration + xqc_packet_number_t largest_pn = 0; + if (packet_in->pi_path_id != XQC_UNKNOWN_PATH_ID) { + xqc_path_ctx_t *path = xqc_conn_find_path_by_path_id(conn, packet_in->pi_path_id); + if (path == NULL) { + xqc_log(conn->log, XQC_LOG_ERROR, + "|canno find the path|path_id:%ui|", + packet_in->pi_path_id); + return -XQC_EMP_PATH_NOT_FOUND; + } + xqc_pn_ctl_t *pn_ctl = xqc_get_pn_ctl(conn, path); + xqc_pkt_num_space_t pns = packet_in->pi_pkt.pkt_pns; + largest_pn = xqc_recv_record_largest(&pn_ctl->ctl_recv_record[pns]); } - xqc_pn_ctl_t *pn_ctl = xqc_get_pn_ctl(conn, path); - - xqc_pkt_num_space_t pns = packet_in->pi_pkt.pkt_pns; - xqc_packet_number_t largest_pn = xqc_recv_record_largest(&pn_ctl->ctl_recv_record[pns]); + packet_in->pi_pkt.pkt_num = xqc_packet_decode_packet_number(largest_pn, truncated_pn, pktno_len * 8); + xqc_log(conn->log, XQC_LOG_DEBUG, "|largest_pn:%ui|", largest_pn); + /* check key phase, determine weather to update read keys */ xqc_uint_t key_phase = XQC_PACKET_SHORT_HEADER_KEY_PHASE(header); if (packet_in->pi_pkt.pkt_type == XQC_PTYPE_SHORT_HEADER && level == XQC_ENC_LEV_1RTT @@ -767,8 +814,9 @@ xqc_packet_decrypt(xqc_connection_t *conn, xqc_packet_in_t *packet_in) } /* decrypt packet payload */ + // TODO: MPQUIC fix migration uint32_t nonce_path_id = (conn->enable_multipath) ? - (uint32_t)packet_in->pi_path_id : 0; + (uint32_t)packet_in->pi_pkt.pkt_dcid.cid_seq_num : 0; ret = xqc_tls_decrypt_payload(conn->tls, level, packet_in->pi_pkt.pkt_num, nonce_path_id, header, header_len, payload, payload_len, @@ -1237,6 +1285,8 @@ xqc_packet_parse_long_header(xqc_connection_t *c, xqc_packet_t *packet = &packet_in->pi_pkt; xqc_int_t ret = XQC_ERROR; + packet_in->pi_path_id = XQC_INITIAL_PATH_ID; + if (XQC_BUFF_LEFT_SIZE(pos, end) < XQC_PACKET_LONG_HEADER_PREFIX_LENGTH + 2) { return -XQC_EILLPKT; } diff --git a/src/transport/xqc_reinjection.c b/src/transport/xqc_reinjection.c index c4e4399c3..ab07de1a2 100644 --- a/src/transport/xqc_reinjection.c +++ b/src/transport/xqc_reinjection.c @@ -23,32 +23,6 @@ #include "xquic/xqc_errno.h" -xqc_bool_t -xqc_packet_can_reinject(xqc_packet_out_t *packet_out) -{ - if (!(packet_out->po_frame_types & XQC_FRAME_BIT_STREAM)) { - return XQC_FALSE; - } - - if (packet_out->po_flag & XQC_POF_NOT_REINJECT) { - return XQC_FALSE; - } - - if (XQC_MP_PKT_REINJECTED(packet_out) - && (packet_out->po_origin? XQC_MP_PKT_REINJECTED(packet_out->po_origin): XQC_TRUE)) - { - return XQC_FALSE; - } - - /* DO NOT reinject non-inflight packets (those were old copies of retx pkts) */ - if (!(packet_out->po_flag & XQC_POF_IN_FLIGHT)) { - return XQC_FALSE; - } - - return XQC_TRUE; -} - - void xqc_associate_packet_with_reinjection(xqc_packet_out_t *reinj_origin, xqc_packet_out_t *reinj_replica) @@ -94,7 +68,7 @@ xqc_packet_out_replicate(xqc_packet_out_t *dst, xqc_packet_out_t *src) dst->po_user_data = src->po_user_data; } -static xqc_int_t +xqc_int_t xqc_conn_try_reinject_packet(xqc_connection_t *conn, xqc_packet_out_t *packet_out) { xqc_path_ctx_t *path = conn->scheduler_callback->xqc_scheduler_get_path(conn->scheduler, conn, packet_out, 1, 1, NULL); @@ -146,7 +120,7 @@ xqc_conn_reinject_unack_packets(xqc_connection_t *conn, xqc_reinjection_mode_t m && conn->reinj_callback->xqc_reinj_ctl_can_reinject && conn->reinj_callback->xqc_reinj_ctl_can_reinject(conn->reinj_ctl, packet_out, mode)) { - + if (xqc_conn_try_reinject_packet(conn, packet_out) != XQC_OK) { continue; } diff --git a/src/transport/xqc_reinjection.h b/src/transport/xqc_reinjection.h index 7fdd2afbd..ae33d9536 100644 --- a/src/transport/xqc_reinjection.h +++ b/src/transport/xqc_reinjection.h @@ -18,14 +18,15 @@ #define XQC_MP_FIRST_FRAME_OFFSET 128 * 1024 /* 128k */ #define XQC_MP_PKT_REINJECTED(po) (po->po_flag & (XQC_POF_REINJECTED_ORIGIN | XQC_POF_REINJECTED_REPLICA)) -xqc_bool_t xqc_packet_can_reinject(xqc_packet_out_t *packet_out); - void xqc_associate_packet_with_reinjection(xqc_packet_out_t *reinj_origin, xqc_packet_out_t *reinj_replica); void xqc_conn_reinject_unack_packets(xqc_connection_t *conn, xqc_reinjection_mode_t mode); +xqc_int_t xqc_conn_try_reinject_packet(xqc_connection_t *conn, + xqc_packet_out_t *packet_out); + #endif /* XQC_REINJECTION_H */ diff --git a/src/transport/xqc_send_ctl.c b/src/transport/xqc_send_ctl.c index 1ac5e19fe..b9f9e3037 100644 --- a/src/transport/xqc_send_ctl.c +++ b/src/transport/xqc_send_ctl.c @@ -19,6 +19,7 @@ #include "src/transport/xqc_pacing.h" #include "src/transport/xqc_utils.h" #include "src/transport/xqc_datagram.h" +#include "src/transport/xqc_reinjection.h" int xqc_send_ctl_may_remove_unacked_dgram(xqc_connection_t *conn, xqc_packet_out_t *po) @@ -120,7 +121,6 @@ xqc_send_ctl_create(xqc_path_ctx_t *path) send_ctl->ctl_conn = conn; send_ctl->ctl_pto_count = 0; - send_ctl->ctl_pto_count_since_last_tra_path_status_changed = 0; send_ctl->ctl_minrtt = XQC_MAX_UINT32_VALUE; send_ctl->ctl_srtt = XQC_kInitialRtt * 1000; send_ctl->ctl_rttvar = XQC_kInitialRtt * 1000 / 2; @@ -206,7 +206,6 @@ xqc_send_ctl_reset(xqc_send_ctl_t *send_ctl) xqc_path_ctx_t *path = send_ctl->ctl_path; send_ctl->ctl_pto_count = 0; - send_ctl->ctl_pto_count_since_last_tra_path_status_changed = 0; send_ctl->ctl_minrtt = XQC_MAX_UINT32_VALUE; send_ctl->ctl_srtt = XQC_kInitialRtt * 1000; send_ctl->ctl_rttvar = XQC_kInitialRtt * 1000 / 2; @@ -537,7 +536,7 @@ xqc_send_ctl_increase_inflight(xqc_connection_t *conn, xqc_packet_out_t *packet_ { xqc_path_ctx_t *path = xqc_conn_find_path_by_path_id(conn, packet_out->po_path_id); if (path == NULL) { - xqc_log(conn->log, XQC_LOG_WARN, "|can't find path by id|%L|", packet_out->po_path_id); + xqc_log(conn->log, XQC_LOG_WARN, "|can't find path by id|%ui|", packet_out->po_path_id); return; } @@ -556,7 +555,7 @@ xqc_send_ctl_decrease_inflight(xqc_connection_t *conn, xqc_packet_out_t *packet_ { xqc_path_ctx_t *path = xqc_conn_find_path_by_path_id(conn, packet_out->po_path_id); if (path == NULL) { - xqc_log(conn->log, XQC_LOG_WARN, "|can't find path by id|%L|", packet_out->po_path_id); + xqc_log(conn->log, XQC_LOG_WARN, "|can't find path by id|%ui|", packet_out->po_path_id); return; } @@ -583,7 +582,6 @@ xqc_send_ctl_on_pns_discard(xqc_send_ctl_t *send_ctl, xqc_pkt_num_space_t pns) send_ctl->ctl_time_of_last_sent_ack_eliciting_packet[pns] = 0; send_ctl->ctl_loss_time[pns] = 0; send_ctl->ctl_pto_count = 0; - send_ctl->ctl_pto_count_since_last_tra_path_status_changed = 0; xqc_log(send_ctl->ctl_conn->log, XQC_LOG_INFO, "|xqc_send_ctl_set_loss_detection_timer on discard pns:%ud", pns); xqc_send_ctl_set_loss_detection_timer(send_ctl); } @@ -722,7 +720,6 @@ xqc_send_ctl_on_packet_sent(xqc_send_ctl_t *send_ctl, xqc_pn_ctl_t *pn_ctl, xqc_ packet_out->po_flag &= ~XQC_POF_TLP; } - /* record dgram stats */ if (packet_out->po_frame_types & XQC_FRAME_BIT_DATAGRAM) { send_ctl->ctl_conn->dgram_stats.total_dgram++; @@ -934,7 +931,6 @@ xqc_send_ctl_on_ack_received(xqc_send_ctl_t *send_ctl, xqc_pn_ctl_t *pn_ctl, xqc */ if (xqc_conn_peer_complete_address_validation(conn)) { send_ctl->ctl_pto_count = 0; - send_ctl->ctl_pto_count_since_last_tra_path_status_changed = 0; } xqc_log(conn->log, XQC_LOG_DEBUG, "|xqc_send_ctl_set_loss_detection_timer|acked|pto_count:%ud|", send_ctl->ctl_pto_count); @@ -1076,6 +1072,8 @@ xqc_send_ctl_latest_rtt_tracking(xqc_send_ctl_t *send_ctl, xqc_usec_t *latest_rt void xqc_send_ctl_update_rtt(xqc_send_ctl_t *send_ctl, xqc_usec_t *latest_rtt, xqc_usec_t ack_delay) { + xqc_connection_t *conn = send_ctl->ctl_conn; + xqc_log(send_ctl->ctl_conn->log, XQC_LOG_DEBUG, "|before update rtt|conn:%p|srtt:%ui|rttvar:%ui|minrtt:%ui|latest_rtt:%ui|ack_delay:%ui|", send_ctl->ctl_conn, send_ctl->ctl_srtt, send_ctl->ctl_rttvar, send_ctl->ctl_minrtt, *latest_rtt, ack_delay); @@ -1202,6 +1200,7 @@ xqc_send_ctl_detect_lost(xqc_send_ctl_t *send_ctl, xqc_send_queue_t *send_queue, xqc_packet_number_t lost_pn = xqc_send_ctl_get_lost_sent_pn(send_ctl, pns); xqc_int_t repair_dgram = 0; + xqc_reinjection_mode_t mode = conn->conn_settings.mp_enable_reinjection & XQC_REINJ_UNACK_BEFORE_SCHED; xqc_list_for_each_safe(pos, next, &send_queue->sndq_unacked_packets[pns]) { po = xqc_list_entry(pos, xqc_packet_out_t, po_list); @@ -1226,6 +1225,22 @@ xqc_send_ctl_detect_lost(xqc_send_ctl_t *send_ctl, xqc_send_queue_t *send_queue, || (lost_pn != XQC_MAX_UINT64_VALUE && po->po_pkt.pkt_num <= lost_pn)) { if (po->po_flag & XQC_POF_IN_FLIGHT) { + + /* reinjection */ + if (conn->enable_multipath + && conn->reinj_callback + && conn->reinj_callback->xqc_reinj_ctl_can_reinject + && conn->reinj_callback->xqc_reinj_ctl_can_reinject(conn->reinj_ctl, po, mode)) + { + if (xqc_conn_try_reinject_packet(conn, po) == XQC_OK) { + xqc_log(conn->log, XQC_LOG_DEBUG, "|MP|REINJ|reinject lost packets|" + "pkt_num:%ui|size:%ud|pkt_type:%s|frame:%s|", + po->po_pkt.pkt_num, po->po_used_size, + xqc_pkt_type_2_str(po->po_pkt.pkt_type), + xqc_frame_type_2_str(po->po_frame_types)); + } + } + xqc_send_ctl_decrease_inflight(conn, po); if (po->po_frame_types & XQC_FRAME_BIT_DATAGRAM) { @@ -1507,7 +1522,7 @@ xqc_send_ctl_on_packet_acked(xqc_send_ctl_t *send_ctl, /* TODO: fix NEW_CID_RECEIVED */ if (packet_out->po_frame_types & XQC_FRAME_BIT_NEW_CONNECTION_ID) { packet_out->po_frame_types &= ~XQC_FRAME_BIT_NEW_CONNECTION_ID; - conn->conn_flag |= XQC_CONN_FLAG_NEW_CID_RECEIVED; + conn->conn_flag |= XQC_CONN_FLAG_NEW_CID_ACKED; } if (do_cc) { diff --git a/src/transport/xqc_send_ctl.h b/src/transport/xqc_send_ctl.h index f6bb40a28..7ca18f30b 100644 --- a/src/transport/xqc_send_ctl.h +++ b/src/transport/xqc_send_ctl.h @@ -105,14 +105,12 @@ typedef struct xqc_send_ctl_s { xqc_timer_manager_t path_timer_manager; unsigned ctl_pto_count; - unsigned ctl_pto_count_since_last_tra_path_status_changed; unsigned ctl_send_count; unsigned ctl_lost_count; unsigned ctl_tlp_count; unsigned ctl_spurious_loss_count; unsigned ctl_lost_dgram_cnt; - unsigned ctl_send_count_at_last_tra_path_status_changed_time; unsigned ctl_recv_count; diff --git a/src/transport/xqc_stream.c b/src/transport/xqc_stream.c index eda1bd23f..d2517c594 100644 --- a/src/transport/xqc_stream.c +++ b/src/transport/xqc_stream.c @@ -187,8 +187,8 @@ xqc_stream_set_flow_ctl(xqc_stream_t *stream) xqc_connection_t *conn = stream->stream_conn; if ((remote_settings->max_stream_data_bidi_remote - & remote_settings->max_stream_data_bidi_local - & remote_settings->max_stream_data_uni) == 0) + && remote_settings->max_stream_data_bidi_local + && remote_settings->max_stream_data_uni) == XQC_FALSE) { remote_settings = &stream->stream_conn->local_settings; } @@ -252,6 +252,42 @@ xqc_stream_set_flow_ctl(xqc_stream_t *stream) } } + +void +xqc_stream_update_flow_ctl(xqc_stream_t *stream) +{ + xqc_trans_settings_t *remote_settings = &stream->stream_conn->remote_settings; + xqc_connection_t *conn = stream->stream_conn; + + if (conn->conn_type == XQC_CONN_TYPE_CLIENT) { + if (stream->stream_type == XQC_CLI_BID) { + stream->stream_flow_ctl.fc_max_stream_data_can_send = remote_settings->max_stream_data_bidi_remote; + + } else if (stream->stream_type == XQC_SVR_BID) { + /* + * in server transport parameters, + * this applies to streams with the least significant two bits set to 0x1 + */ + stream->stream_flow_ctl.fc_max_stream_data_can_send = remote_settings->max_stream_data_bidi_local; + + } else { + stream->stream_flow_ctl.fc_max_stream_data_can_send = remote_settings->max_stream_data_uni; + } + + } else { /* conn->conn_type == XQC_CONN_TYPE_SERVER */ + if (stream->stream_type == XQC_CLI_BID) { + stream->stream_flow_ctl.fc_max_stream_data_can_send = remote_settings->max_stream_data_bidi_local; + + } else if (stream->stream_type == XQC_SVR_BID) { + stream->stream_flow_ctl.fc_max_stream_data_can_send = remote_settings->max_stream_data_bidi_remote; + + } else { + stream->stream_flow_ctl.fc_max_stream_data_can_send = remote_settings->max_stream_data_uni; + } + } +} + + uint64_t xqc_stream_get_init_max_stream_data(xqc_stream_t *stream) { @@ -281,6 +317,10 @@ int xqc_stream_do_send_flow_ctl(xqc_stream_t *stream) { int ret = XQC_OK; + + xqc_log(stream->stream_conn->log, XQC_LOG_DEBUG, "|conn_flow_ctl|window:%ui|", + stream->stream_conn->conn_flow_ctl.fc_max_data_can_send - stream->stream_conn->conn_flow_ctl.fc_data_sent); + /* connection level */ if (stream->stream_conn->conn_flow_ctl.fc_data_sent + stream->stream_conn->pkt_out_size > stream->stream_conn->conn_flow_ctl.fc_max_data_can_send) { xqc_log(stream->stream_conn->log, XQC_LOG_INFO, "|xqc_stream_send|exceed max_data:%ui|", @@ -311,51 +351,94 @@ xqc_stream_do_recv_flow_ctl(xqc_stream_t *stream) xqc_usec_t now = xqc_monotonic_timestamp(); /* increase recv window */ - xqc_usec_t min_srtt = xqc_conn_get_min_srtt(conn); + xqc_usec_t min_srtt = xqc_conn_get_min_srtt(conn, 0); + xqc_usec_t max_srtt = 0; + uint64_t old_fc_win = 0; /* stream level */ - uint64_t available_window - = stream->stream_flow_ctl.fc_max_stream_data_can_recv - stream->stream_data_in.next_read_offset; + uint64_t available_window = stream->stream_flow_ctl.fc_max_stream_data_can_recv - stream->stream_data_in.next_read_offset; + if (available_window < stream->stream_flow_ctl.fc_stream_recv_window_size / 2) { - if (stream->stream_flow_ctl.fc_last_window_update_time == 0) { - /* first update window */ + + if (!stream->recv_rate_bytes_per_sec) { + if (stream->stream_flow_ctl.fc_last_window_update_time + && (now - stream->stream_flow_ctl.fc_last_window_update_time < 2 * min_srtt)) + { + stream->stream_flow_ctl.fc_stream_recv_window_size + = xqc_min(stream->stream_flow_ctl.fc_stream_recv_window_size * 2, XQC_MAX_RECV_WINDOW); + } + + } else { + + if (!max_srtt) { + max_srtt = xqc_conn_get_max_srtt(conn); + } - } else if (now - stream->stream_flow_ctl.fc_last_window_update_time < 2 * min_srtt) { - stream->stream_flow_ctl.fc_stream_recv_window_size - = xqc_min(stream->stream_flow_ctl.fc_stream_recv_window_size * 2, XQC_MAX_RECV_WINDOW); + old_fc_win = stream->stream_flow_ctl.fc_stream_recv_window_size; + stream->stream_flow_ctl.fc_stream_recv_window_size = stream->recv_rate_bytes_per_sec * max_srtt / 1000000; + stream->stream_flow_ctl.fc_stream_recv_window_size = xqc_max(conn->conn_settings.init_recv_window, stream->stream_flow_ctl.fc_stream_recv_window_size); + stream->stream_flow_ctl.fc_stream_recv_window_size = xqc_min(XQC_MAX_RECV_WINDOW, stream->stream_flow_ctl.fc_stream_recv_window_size); + xqc_log(conn->log, XQC_LOG_DEBUG, + "|stream_level|fc_win_update|old_fc_win:%ui|fc_win:%ui|", + old_fc_win, stream->stream_flow_ctl.fc_stream_recv_window_size); } + stream->stream_flow_ctl.fc_last_window_update_time = now; - stream->stream_flow_ctl.fc_max_stream_data_can_recv - += (stream->stream_flow_ctl.fc_stream_recv_window_size - available_window); - xqc_log(conn->log, XQC_LOG_DEBUG, - "|xqc_write_max_stream_data_to_packet|new_max_data:%ui|stream_max_recv_offset:%ui|next_read_offset:%ui|window_size:%ui|", - stream->stream_flow_ctl.fc_max_stream_data_can_recv, stream->stream_max_recv_offset, - stream->stream_data_in.next_read_offset, stream->stream_flow_ctl.fc_stream_recv_window_size); - xqc_write_max_stream_data_to_packet(conn, stream->stream_id, stream->stream_flow_ctl.fc_max_stream_data_can_recv); + + if (stream->stream_flow_ctl.fc_stream_recv_window_size > available_window) { + stream->stream_flow_ctl.fc_max_stream_data_can_recv += (stream->stream_flow_ctl.fc_stream_recv_window_size - available_window); + xqc_log(conn->log, XQC_LOG_DEBUG, + "|xqc_write_max_stream_data_to_packet|new_max_data:%ui|stream_max_recv_offset:%ui|next_read_offset:%ui|window_size:%ui|", + stream->stream_flow_ctl.fc_max_stream_data_can_recv, stream->stream_max_recv_offset, + stream->stream_data_in.next_read_offset, stream->stream_flow_ctl.fc_stream_recv_window_size); + xqc_write_max_stream_data_to_packet(conn, stream->stream_id, stream->stream_flow_ctl.fc_max_stream_data_can_recv, XQC_PTYPE_SHORT_HEADER); + } } /* connection level */ - available_window - = conn->conn_flow_ctl.fc_max_data_can_recv - conn->conn_flow_ctl.fc_data_read; + available_window = conn->conn_flow_ctl.fc_max_data_can_recv - conn->conn_flow_ctl.fc_data_read; + if (available_window < conn->conn_flow_ctl.fc_recv_windows_size / 2) { - if (conn->conn_flow_ctl.fc_last_window_update_time == 0) { - /* first update window */ - } else if (now - conn->conn_flow_ctl.fc_last_window_update_time < 2 * min_srtt) { - conn->conn_flow_ctl.fc_recv_windows_size - = xqc_min(conn->conn_flow_ctl.fc_recv_windows_size * 2, XQC_MAX_RECV_WINDOW); + if (!conn->conn_settings.recv_rate_bytes_per_sec) { + + if (conn->conn_flow_ctl.fc_last_window_update_time + && (now - conn->conn_flow_ctl.fc_last_window_update_time < 2 * min_srtt)) + { + conn->conn_flow_ctl.fc_recv_windows_size + = xqc_min(conn->conn_flow_ctl.fc_recv_windows_size * 2, XQC_MAX_RECV_WINDOW); + } + + if (conn->conn_flow_ctl.fc_recv_windows_size < 1.5 * stream->stream_flow_ctl.fc_stream_recv_window_size) { + conn->conn_flow_ctl.fc_recv_windows_size = (uint64_t)(1.5 * stream->stream_flow_ctl.fc_stream_recv_window_size); + } + + } else { + + if (!max_srtt) { + max_srtt = xqc_conn_get_max_srtt(conn); + } + + old_fc_win = conn->conn_flow_ctl.fc_recv_windows_size; + conn->conn_flow_ctl.fc_recv_windows_size = conn->conn_settings.recv_rate_bytes_per_sec * max_srtt / 1000000; + conn->conn_flow_ctl.fc_recv_windows_size = xqc_max(XQC_MIN_RECV_WINDOW, conn->conn_flow_ctl.fc_recv_windows_size); + conn->conn_flow_ctl.fc_recv_windows_size = xqc_min(XQC_MAX_RECV_WINDOW, conn->conn_flow_ctl.fc_recv_windows_size); + xqc_log(conn->log, XQC_LOG_DEBUG, + "|conn_level|fc_win_update|old_fc_win:%ui|fc_win:%ui|", + old_fc_win, conn->conn_flow_ctl.fc_recv_windows_size); + } + conn->conn_flow_ctl.fc_last_window_update_time = now; - if (conn->conn_flow_ctl.fc_recv_windows_size < 1.5 * stream->stream_flow_ctl.fc_stream_recv_window_size) { - conn->conn_flow_ctl.fc_recv_windows_size = (uint64_t)(1.5 * stream->stream_flow_ctl.fc_stream_recv_window_size); + + if (conn->conn_flow_ctl.fc_recv_windows_size > available_window) { + conn->conn_flow_ctl.fc_max_data_can_recv += (conn->conn_flow_ctl.fc_recv_windows_size - available_window); + xqc_log(conn->log, XQC_LOG_DEBUG, + "|xqc_write_max_data_to_packet|new_max_data:%ui|fc_data_recved:%ui|fc_data_read:%ui|window_size:%ui|", + conn->conn_flow_ctl.fc_max_data_can_recv, conn->conn_flow_ctl.fc_data_recved, + conn->conn_flow_ctl.fc_data_read, conn->conn_flow_ctl.fc_recv_windows_size); + xqc_write_max_data_to_packet(conn, conn->conn_flow_ctl.fc_max_data_can_recv); } - conn->conn_flow_ctl.fc_max_data_can_recv - += (conn->conn_flow_ctl.fc_recv_windows_size - available_window); - xqc_log(conn->log, XQC_LOG_DEBUG, - "|xqc_write_max_data_to_packet|new_max_data:%ui|fc_data_recved:%ui|fc_data_read:%ui|window_size:%ui|", - conn->conn_flow_ctl.fc_max_data_can_recv, conn->conn_flow_ctl.fc_data_recved, - conn->conn_flow_ctl.fc_data_read, conn->conn_flow_ctl.fc_recv_windows_size); - xqc_write_max_data_to_packet(conn, conn->conn_flow_ctl.fc_max_data_can_recv); } return XQC_OK; @@ -415,7 +498,8 @@ xqc_stream_do_create_flow_ctl(xqc_connection_t *conn, xqc_stream_id_t stream_id, } xqc_stream_t * -xqc_stream_create(xqc_engine_t *engine, const xqc_cid_t *cid, void *user_data) +xqc_stream_create(xqc_engine_t *engine, const xqc_cid_t *cid, xqc_stream_settings_t *settings, + void *user_data) { xqc_connection_t *conn; xqc_stream_t *stream; @@ -427,7 +511,7 @@ xqc_stream_create(xqc_engine_t *engine, const xqc_cid_t *cid, void *user_data) return NULL; } - stream = xqc_create_stream_with_conn(conn, XQC_UNDEFINE_STREAM_ID, XQC_CLI_BID, user_data); + stream = xqc_create_stream_with_conn(conn, XQC_UNDEFINE_STREAM_ID, XQC_CLI_BID, settings, user_data); if (!stream) { xqc_log(engine->log, XQC_LOG_ERROR, "|xqc_create_stream_with_conn error|"); return NULL; @@ -462,13 +546,13 @@ xqc_stream_create_with_direction(xqc_connection_t *conn, /* create stream */ return xqc_create_stream_with_conn(conn, XQC_UNDEFINE_STREAM_ID, type, - user_data); + NULL, user_data); } xqc_stream_t * xqc_create_stream_with_conn(xqc_connection_t *conn, xqc_stream_id_t stream_id, - xqc_stream_type_t stream_type, void *user_data) + xqc_stream_type_t stream_type, xqc_stream_settings_t *settings, void *user_data) { xqc_int_t ret; @@ -533,6 +617,16 @@ xqc_create_stream_with_conn(xqc_connection_t *conn, xqc_stream_id_t stream_id, xqc_stream_ready_to_write(stream); } + stream->recv_rate_bytes_per_sec = 0; + + if (settings) { + if (conn->conn_settings.enable_stream_rate_limit + && stream->stream_type == XQC_CLI_BID) + { + stream->recv_rate_bytes_per_sec = settings->recv_rate_bytes_per_sec; + } + } + if (stream->stream_if->stream_create_notify) { ret = stream->stream_if->stream_create_notify(stream, stream->user_data); if (XQC_OK != ret) { @@ -738,7 +832,7 @@ xqc_passive_create_stream(xqc_connection_t *conn, xqc_stream_id_t stream_id, voi } } - xqc_stream_t *stream = xqc_create_stream_with_conn(conn, stream_id, 0, user_data); + xqc_stream_t *stream = xqc_create_stream_with_conn(conn, stream_id, 0, NULL, user_data); if (stream == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_create_stream_with_conn error|stream_id:%ui|", stream_id); XQC_CONN_ERR(conn, TRA_INTERNAL_ERROR); @@ -748,6 +842,46 @@ xqc_passive_create_stream(xqc_connection_t *conn, xqc_stream_id_t stream_id, voi return stream; } + +xqc_int_t +xqc_stream_update_settings(xqc_stream_t *stream, + xqc_stream_settings_t *settings) +{ + xqc_connection_t *conn = NULL; + xqc_usec_t max_srtt = 0; + uint64_t old_fc_win = 0, new_offset = 0; + + if (stream && settings + && settings->recv_rate_bytes_per_sec) + { + conn = stream->stream_conn; + if (conn->conn_settings.enable_stream_rate_limit) { + stream->recv_rate_bytes_per_sec = settings->recv_rate_bytes_per_sec; + max_srtt = xqc_conn_get_max_srtt(conn); + old_fc_win = stream->stream_flow_ctl.fc_stream_recv_window_size; + stream->stream_flow_ctl.fc_stream_recv_window_size = stream->recv_rate_bytes_per_sec * max_srtt / 1000000; + stream->stream_flow_ctl.fc_stream_recv_window_size = xqc_max(conn->conn_settings.init_recv_window, stream->stream_flow_ctl.fc_stream_recv_window_size); + stream->stream_flow_ctl.fc_stream_recv_window_size = xqc_min(XQC_MAX_RECV_WINDOW, stream->stream_flow_ctl.fc_stream_recv_window_size); + xqc_log(conn->log, XQC_LOG_DEBUG, + "|fc_win_update|old_fc_win:%ui|fc_win:%ui|", + old_fc_win, stream->stream_flow_ctl.fc_stream_recv_window_size); + new_offset = stream->stream_data_in.next_read_offset + stream->stream_flow_ctl.fc_stream_recv_window_size; + + if(new_offset > stream->stream_flow_ctl.fc_max_stream_data_can_recv) { + stream->stream_flow_ctl.fc_max_stream_data_can_recv = new_offset; + xqc_log(conn->log, XQC_LOG_DEBUG, + "|new_max_data:%ui|stream_max_recv_offset:%ui|next_read_offset:%ui|window_size:%ui|", + stream->stream_flow_ctl.fc_max_stream_data_can_recv, stream->stream_max_recv_offset, + stream->stream_data_in.next_read_offset, stream->stream_flow_ctl.fc_stream_recv_window_size); + xqc_write_max_stream_data_to_packet(conn, stream->stream_id, stream->stream_flow_ctl.fc_max_stream_data_can_recv, XQC_PTYPE_NUM); + } + return XQC_OK; + } + } + + return -XQC_EPARAM; +} + xqc_int_t xqc_read_crypto_stream(xqc_stream_t *stream) { diff --git a/src/transport/xqc_stream.h b/src/transport/xqc_stream.h index 2297a3f65..e68de280e 100644 --- a/src/transport/xqc_stream.h +++ b/src/transport/xqc_stream.h @@ -149,6 +149,8 @@ struct xqc_stream_s { xqc_path_metrics_t paths_info[XQC_MAX_PATHS_COUNT]; uint8_t stream_mp_usage_schedule; uint8_t stream_mp_usage_reinject; + + uint64_t recv_rate_bytes_per_sec; }; static inline xqc_stream_type_t @@ -170,7 +172,7 @@ xqc_stream_is_uni(xqc_stream_id_t stream_id) } xqc_stream_t *xqc_create_stream_with_conn (xqc_connection_t *conn, xqc_stream_id_t stream_id, - xqc_stream_type_t stream_type, void *user_data); + xqc_stream_type_t stream_type, xqc_stream_settings_t *settings, void *user_data); void xqc_destroy_stream(xqc_stream_t *stream); @@ -196,14 +198,14 @@ xqc_stream_t *xqc_find_stream_by_id(xqc_stream_id_t stream_id, xqc_id_hash_table void xqc_stream_set_flow_ctl(xqc_stream_t *stream); +void xqc_stream_update_flow_ctl(xqc_stream_t *stream); + int xqc_stream_do_send_flow_ctl(xqc_stream_t *stream); int xqc_stream_do_recv_flow_ctl(xqc_stream_t *stream); int xqc_stream_do_create_flow_ctl(xqc_connection_t *conn, xqc_stream_id_t stream_id, xqc_stream_type_t stream_type); -uint64_t xqc_stream_get_init_max_stream_data(xqc_stream_t *stream); - xqc_stream_t *xqc_passive_create_stream(xqc_connection_t *conn, xqc_stream_id_t stream_id, void *user_data); xqc_stream_t *xqc_create_crypto_stream(xqc_connection_t *conn, xqc_encrypt_level_t encrypt_level, void *user_data); diff --git a/src/transport/xqc_timer.c b/src/transport/xqc_timer.c index 5f3c70e26..cc4e66e1d 100644 --- a/src/transport/xqc_timer.c +++ b/src/transport/xqc_timer.c @@ -102,7 +102,6 @@ xqc_timer_loss_detection_timeout(xqc_timer_type_t type, xqc_usec_t now, void *us } send_ctl->ctl_pto_count++; - send_ctl->ctl_pto_count_since_last_tra_path_status_changed++; xqc_log(conn->log, XQC_LOG_DEBUG, "|xqc_send_ctl_set_loss_detection_timer|PTO|conn:%p|pto_count:%ud", conn, send_ctl->ctl_pto_count); xqc_send_ctl_set_loss_detection_timer(send_ctl); @@ -261,6 +260,12 @@ xqc_timer_retire_cid_timeout(xqc_timer_type_t type, xqc_usec_t now, void *user_d return; } + xqc_log(conn->log, XQC_LOG_DEBUG, + "|retired->removed|cid:%s|seq:%ui|len:%d|", + xqc_scid_str(&inner_cid->cid), + inner_cid->cid.cid_seq_num, + inner_cid->cid.cid_len); + // xqc_list_del(pos); // xqc_free(inner_cid); diff --git a/src/transport/xqc_transport_params.c b/src/transport/xqc_transport_params.c index 1a606f15d..ab54839c8 100644 --- a/src/transport/xqc_transport_params.c +++ b/src/transport/xqc_transport_params.c @@ -157,9 +157,16 @@ xqc_transport_params_calc_length(const xqc_transport_params_t *params, } if (params->enable_multipath) { - len += xqc_put_varint_len(XQC_TRANSPORT_PARAM_ENABLE_MULTIPATH) + - xqc_put_varint_len(xqc_put_varint_len(params->enable_multipath)) + - xqc_put_varint_len(params->enable_multipath); + if (params->multipath_version == XQC_MULTIPATH_05) { + /* enable_multipath (-draft05) is zero-length transport parameter */ + len += xqc_put_varint_len(XQC_TRANSPORT_PARAM_ENABLE_MULTIPATH_05) + + xqc_put_varint_len(0); + + } else { + len += xqc_put_varint_len(XQC_TRANSPORT_PARAM_ENABLE_MULTIPATH_04) + + xqc_put_varint_len(xqc_put_varint_len(params->enable_multipath)) + + xqc_put_varint_len(params->enable_multipath); + } } if (params->max_datagram_frame_size) { @@ -332,8 +339,12 @@ xqc_encode_transport_params(const xqc_transport_params_t *params, } if (params->enable_multipath) { - p = xqc_put_varint_param(p, XQC_TRANSPORT_PARAM_ENABLE_MULTIPATH, - params->enable_multipath); + if (params->multipath_version == XQC_MULTIPATH_05) { + p = xqc_put_zero_length_param(p, XQC_TRANSPORT_PARAM_ENABLE_MULTIPATH_05); + + } else { + p = xqc_put_varint_param(p, XQC_TRANSPORT_PARAM_ENABLE_MULTIPATH_04, params->enable_multipath); + } } if ((size_t)(p - out) != len) { @@ -576,7 +587,19 @@ static xqc_int_t xqc_decode_enable_multipath(xqc_transport_params_t *params, xqc_transport_params_type_t exttype, const uint8_t *p, const uint8_t *end, uint64_t param_type, uint64_t param_len) { - XQC_DECODE_VINT_VALUE(¶ms->enable_multipath, p, end); + if (param_type == XQC_TRANSPORT_PARAM_ENABLE_MULTIPATH_05) { + /* enable_multipath param is a zero-length value, presentation means enable */ + params->enable_multipath = 1; + params->multipath_version = XQC_MULTIPATH_05; + return XQC_OK; + } else if (param_type == XQC_TRANSPORT_PARAM_ENABLE_MULTIPATH_04) { + if (params->multipath_version > XQC_MULTIPATH_04) { + return XQC_OK; + } + params->multipath_version = XQC_MULTIPATH_04; + XQC_DECODE_VINT_VALUE(¶ms->enable_multipath, p, end); + } + return XQC_OK; } static xqc_int_t @@ -643,8 +666,9 @@ xqc_trans_param_get_index(uint64_t param_type) case XQC_TRANSPORT_PARAM_NO_CRYPTO: return XQC_TRANSPORT_PARAM_PROTOCOL_MAX; - - case XQC_TRANSPORT_PARAM_ENABLE_MULTIPATH: + + case XQC_TRANSPORT_PARAM_ENABLE_MULTIPATH_04: + case XQC_TRANSPORT_PARAM_ENABLE_MULTIPATH_05: return XQC_TRANSPORT_PARAM_PROTOCOL_MAX + 1; case XQC_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE: @@ -741,6 +765,7 @@ xqc_decode_transport_params(xqc_transport_params_t *params, params->max_datagram_frame_size = 0; params->enable_multipath = 0; + params->multipath_version = XQC_ERR_MULTIPATH_VERSION; while (p < end) { ret = xqc_decode_one_transport_param(params, exttype, &p, end); diff --git a/src/transport/xqc_transport_params.h b/src/transport/xqc_transport_params.h index c503c2105..98dde9522 100644 --- a/src/transport/xqc_transport_params.h +++ b/src/transport/xqc_transport_params.h @@ -24,6 +24,7 @@ #define XQC_MAX_TRANSPORT_PARAM_BUF_LEN 512 + /** * @brief transport parameter type */ @@ -67,8 +68,10 @@ typedef enum { /* do no cryption on 0-RTT and 1-RTT packets */ XQC_TRANSPORT_PARAM_NO_CRYPTO = 0x1000, + /* multipath quic attributes */ - XQC_TRANSPORT_PARAM_ENABLE_MULTIPATH = 0x0f739bbc1b666d04, + XQC_TRANSPORT_PARAM_ENABLE_MULTIPATH_04 = 0x0f739bbc1b666d04, + XQC_TRANSPORT_PARAM_ENABLE_MULTIPATH_05 = 0x0f739bbc1b666d05, /* upper limit of params defined by xquic */ XQC_TRANSPORT_PARAM_UNKNOWN, @@ -133,13 +136,17 @@ typedef struct { /** * enable_multipath is a self-defined experimental transport parameter by xquic, which will * enable multipath quic if enable_multipath is set to be 1. - * https://datatracker.ietf.org/doc/html/draft-ietf-quic-multipath-04#section-3 + + * https://datatracker.ietf.org/doc/html/draft-ietf-quic-multipath-05#section-3 * enable_multipath is designed to be effective only on current connection and do not apply to * future connections, storing this parameter and recover on future connections is prohibited. * NOTICE: enable_multipath MIGHT be modified or removed as it is not an official parameter */ uint64_t enable_multipath; + + xqc_multipath_version_t multipath_version; + } xqc_transport_params_t; diff --git a/tests/test_client.c b/tests/test_client.c index ce69a64ff..536e04083 100644 --- a/tests/test_client.c +++ b/tests/test_client.c @@ -243,6 +243,7 @@ char g_headers[MAX_HEADER][256]; int g_header_cnt = 0; int g_ping_id = 1; int g_enable_multipath = 0; +xqc_multipath_version_t g_multipath_version = XQC_MULTIPATH_04; int g_enable_reinjection = 0; int g_verify_cert = 0; int g_verify_cert_allow_self_sign = 0; @@ -2618,6 +2619,11 @@ xqc_client_request_send(xqc_h3_request_t *h3_request, user_stream_t *user_stream fin = 0; } + if (g_test_case == 109) { + xqc_stream_settings_t settings = {.recv_rate_bytes_per_sec = 100000000}; + xqc_h3_request_update_settings(h3_request, &settings); + } + /* send body */ if (user_stream->send_offset < user_stream->send_body_len) { @@ -2976,7 +2982,7 @@ xqc_client_request_close_notify(xqc_h3_request_t *h3_request, void *user_data) if (g_req_cnt < g_req_max) { user_stream = calloc(1, sizeof(user_stream_t)); user_stream->user_conn = user_conn; - user_stream->h3_request = xqc_h3_request_create(p_ctx->engine, &user_conn->cid, user_stream); + user_stream->h3_request = xqc_h3_request_create(p_ctx->engine, &user_conn->cid, NULL, user_stream); if (user_stream->h3_request == NULL) { printf("xqc_h3_request_create error\n"); free(user_stream); @@ -3034,6 +3040,9 @@ xqc_client_socket_read_handler(user_conn_t *user_conn, int fd) xqc_int_t ret; ssize_t recv_size = 0; ssize_t recv_sum = 0; + uint64_t path_id = XQC_UNKNOWN_PATH_ID; + xqc_user_path_t *path; + int i; client_ctx_t *p_ctx; if (g_test_qch_mode) { @@ -3042,6 +3051,13 @@ xqc_client_socket_read_handler(user_conn_t *user_conn, int fd) p_ctx = &ctx; } + for (i = 0; i < g_multi_interface_cnt; i++) { + path = &g_client_path[i]; + if (path->path_fd == fd || path->rebinding_path_fd == fd) { + path_id = path->path_id; + } + } + #ifdef __linux__ int batch = 0; if (batch) { @@ -3078,10 +3094,17 @@ xqc_client_socket_read_handler(user_conn_t *user_conn, int fd) for (int i = 0; i < retval; i++) { recv_sum += msgs[i].msg_len; +#ifdef XQC_NO_PID_PACKET_PROCESS ret = xqc_engine_packet_process(p_ctx->engine, iovecs[i].iov_base, msgs[i].msg_len, user_conn->local_addr, user_conn->local_addrlen, user_conn->peer_addr, user_conn->peer_addrlen, (xqc_msec_t)recv_time, user_conn); +#else + ret = xqc_engine_packet_process(p_ctx->engine, iovecs[i].iov_base, msgs[i].msg_len, + user_conn->local_addr, user_conn->local_addrlen, + user_conn->peer_addr, user_conn->peer_addrlen, + path_id, (xqc_msec_t)recv_time, user_conn); +#endif if (ret != XQC_OK) { printf("xqc_server_read_handler: packet process err, ret: %d\n", ret); @@ -3169,11 +3192,17 @@ xqc_client_socket_read_handler(user_conn_t *user_conn, int fd) continue; } } - +#ifdef XQC_NO_PID_PACKET_PROCESS ret = xqc_engine_packet_process(p_ctx->engine, packet_buf, recv_size, user_conn->local_addr, user_conn->local_addrlen, user_conn->peer_addr, user_conn->peer_addrlen, (xqc_msec_t)recv_time, user_conn); +#else + ret = xqc_engine_packet_process(p_ctx->engine, packet_buf, recv_size, + user_conn->local_addr, user_conn->local_addrlen, + user_conn->peer_addr, user_conn->peer_addrlen, + path_id, (xqc_msec_t)recv_time, user_conn); +#endif if (ret != XQC_OK) { printf("xqc_client_read_handler: packet process err, ret: %d\n", ret); return; @@ -3305,7 +3334,7 @@ xqc_client_request_callback(int fd, short what, void *arg) user_stream_t *user_stream = calloc(1, sizeof(user_stream_t)); user_stream->user_conn = user_conn; if (user_conn->h3 == 0 || user_conn->h3 == 2) { - user_stream->h3_request = xqc_h3_request_create(ctx->engine, &user_conn->cid, user_stream); + user_stream->h3_request = xqc_h3_request_create(ctx->engine, &user_conn->cid, NULL, user_stream); if (user_stream->h3_request == NULL) { printf("xqc_h3_request_create error\n"); return; @@ -3358,7 +3387,7 @@ xqc_client_timeout_callback(int fd, short what, void *arg) memset(user_stream, 0, sizeof(user_stream_t)); user_stream->user_conn = user_conn; printf("gtest 15: restart from idle!\n"); - user_stream->stream = xqc_stream_create(ctx.engine, &(user_conn->cid), user_stream); + user_stream->stream = xqc_stream_create(ctx.engine, &(user_conn->cid), NULL, user_stream); if (user_stream->stream == NULL) { printf("xqc_stream_create error\n"); goto conn_close; @@ -3417,7 +3446,7 @@ xqc_client_path_callback(int fd, short what, void *arg) b_add_path = 1; uint64_t path_id = 0; - int ret = xqc_conn_create_path(ctx.engine, &(user_conn->cid), &path_id); + int ret = xqc_conn_create_path(ctx.engine, &(user_conn->cid), &path_id, 0); if (ret < 0) { printf("not support mp, xqc_conn_create_path err = %d\n", ret); @@ -3469,14 +3498,14 @@ xqc_client_epoch_callback(int fd, short what, void *arg) user_stream->last_recv_log_time = now(); user_stream->recv_log_bytes = 0; if (user_conn->h3 == 0 || user_conn->h3 == 2) { - user_stream->h3_request = xqc_h3_request_create(ctx.engine, &user_conn->cid, user_stream); + user_stream->h3_request = xqc_h3_request_create(ctx.engine, &user_conn->cid, NULL, user_stream); if (user_stream->h3_request == NULL) { printf("xqc_h3_request_create error\n"); continue; } xqc_client_request_send(user_stream->h3_request, user_stream); } else { - user_stream->stream = xqc_stream_create(ctx.engine, &user_conn->cid, user_stream); + user_stream->stream = xqc_stream_create(ctx.engine, &user_conn->cid, NULL, user_stream); if (user_stream->stream == NULL) { printf("xqc_stream_create error\n"); continue; @@ -3669,7 +3698,7 @@ xqc_client_ready_to_create_path(const xqc_cid_t *cid, continue; } - int ret = xqc_conn_create_path(ctx.engine, &(user_conn->cid), &path_id); + int ret = xqc_conn_create_path(ctx.engine, &(user_conn->cid), &path_id, 0); if (ret < 0) { printf("not support mp, xqc_conn_create_path err = %d\n", ret); @@ -3804,7 +3833,7 @@ static void xqc_client_concurrent_callback(int fd, short what, void *arg){ user_stream_t *user_stream = calloc(1, sizeof(user_stream_t)); user_stream->user_conn = user_conn; if (user_conn->h3 == 0 || user_conn->h3 == 2) { - user_stream->h3_request = xqc_h3_request_create(ctx->engine, cid, user_stream); + user_stream->h3_request = xqc_h3_request_create(ctx->engine, cid, NULL, user_stream); if (user_stream->h3_request == NULL) { printf("xqc_h3_request_create error\n"); continue; @@ -3932,6 +3961,7 @@ void usage(int argc, char *argv[]) { " -N No encryption\n" " -6 IPv6\n" " -M Enable multi-path on. |\n" +" -v Multipath Version Negotiation.\n" " -i Multi-path interface. e.g. -i interface1 -i interface2.\n" " -R Enable reinjection. Default is 0, no reinjection.\n" " -V Force cert verification. 0: don't allow self-signed cert. 1: allow self-signed cert.\n" @@ -3986,6 +4016,7 @@ int main(int argc, char *argv[]) { int pacing_on = 0; int transport = 0; int use_1rtt = 0; + uint64_t rate_limit = 0; strcpy(g_log_path, "./clog"); @@ -4000,11 +4031,12 @@ int main(int argc, char *argv[]) { {"dgram_qos", required_argument, &long_opt_index, 4}, {"pmtud", required_argument, &long_opt_index, 5}, {"mp_ping", required_argument, &long_opt_index, 6}, + {"rate_limit", required_argument, &long_opt_index, 7}, {0, 0, 0, 0} }; int ch = 0; - while ((ch = getopt_long(argc, argv, "a:p:P:n:c:Ct:T:1s:w:r:l:Ed:u:H:h:Gx:6NMR:i:V:q:o:fe:F:D:b:B:J:Q:U:Ayz", long_opts, NULL)) != -1) { + while ((ch = getopt_long(argc, argv, "a:p:P:n:c:Ct:T:1s:w:r:l:Ed:u:H:h:Gx:6NMR:i:V:v:q:o:fe:F:D:b:B:J:Q:U:Ayz", long_opts, NULL)) != -1) { switch (ch) { case 'U': printf("option send_datagram 0 (off), 1 (on), 2(on + batch): %s\n", optarg); @@ -4145,6 +4177,16 @@ int main(int argc, char *argv[]) { printf("option enable multi-path: %s\n", optarg); g_enable_multipath = 1; break; + + case 'v': /* Negotiate multipath version. 4: Multipath-04. 5: Multipath-05*/ + printf("option multipath version: %s\n", optarg); + if (atoi(optarg) == 4) { + g_multipath_version = XQC_MULTIPATH_04; + + } else if (atoi(optarg) == 5) { + g_multipath_version = XQC_MULTIPATH_05; + } + break; case 'R': printf("option enable reinjection: %s\n", "on"); g_enable_reinjection = atoi(optarg); @@ -4263,6 +4305,11 @@ int main(int argc, char *argv[]) { printf("option g_mp_ping_on: %d\n", g_mp_ping_on); break; + case 7: + rate_limit = atoi(optarg); + printf("option rate_limit: %"PRIu64" Bps\n", rate_limit); + break; + default: break; } @@ -4384,10 +4431,18 @@ int main(int argc, char *argv[]) { .keyupdate_pkt_threshold = 0, .max_datagram_frame_size = g_max_dgram_size, .enable_multipath = g_enable_multipath, + .multipath_version = g_multipath_version, .marking_reinjection = 1, .mp_ping_on = g_mp_ping_on, + .recv_rate_bytes_per_sec = rate_limit, }; + xqc_stream_settings_t stream_settings = { .recv_rate_bytes_per_sec = 0 }; + + if (g_test_case == 109) { + conn_settings.enable_stream_rate_limit = 1; + stream_settings.recv_rate_bytes_per_sec = 500000; + } if (g_test_case == 400) { //low_delay @@ -4640,6 +4695,12 @@ int main(int argc, char *argv[]) { conn_settings.scheduler_callback = xqc_backup_scheduler_cb; } + if (g_test_case == 501) { + conn_settings.scheduler_callback = xqc_backup_scheduler_cb; + conn_settings.mp_enable_reinjection = 0; + conn_settings.standby_path_probe_timeout = 500; + } + unsigned char token[XQC_MAX_TOKEN_LEN]; int token_len = XQC_MAX_TOKEN_LEN; token_len = xqc_client_read_token(token, token_len); @@ -4731,6 +4792,10 @@ int main(int argc, char *argv[]) { } + if (g_test_case == 501) { + goto skip_data; + } + if (g_test_case >= 300 && g_test_case < 400 && user_conn->h3 == 2) { // for h3 bytestream testcases // send h3 requests @@ -4743,7 +4808,7 @@ int main(int argc, char *argv[]) { user_stream->user_conn = user_conn; user_stream->last_recv_log_time = now(); user_stream->recv_log_bytes = 0; - user_stream->h3_request = xqc_h3_request_create(ctx.engine, cid, user_stream); + user_stream->h3_request = xqc_h3_request_create(ctx.engine, cid, NULL, user_stream); if (user_stream->h3_request == NULL) { printf("xqc_h3_request_create error\n"); continue; @@ -4815,11 +4880,11 @@ int main(int argc, char *argv[]) { if (user_conn->h3 == 0 || user_conn->h3 == 2) { if (g_test_case == 11) { /* create stream fail */ xqc_cid_t tmp; - xqc_h3_request_create(ctx.engine, &tmp, user_stream); + xqc_h3_request_create(ctx.engine, &tmp, NULL, user_stream); continue; } - user_stream->h3_request = xqc_h3_request_create(ctx.engine, cid, user_stream); + user_stream->h3_request = xqc_h3_request_create(ctx.engine, cid, &stream_settings, user_stream); if (user_stream->h3_request == NULL) { printf("xqc_h3_request_create error\n"); continue; @@ -4828,7 +4893,7 @@ int main(int argc, char *argv[]) { xqc_client_request_send(user_stream->h3_request, user_stream); } else { - user_stream->stream = xqc_stream_create(ctx.engine, cid, user_stream); + user_stream->stream = xqc_stream_create(ctx.engine, cid, NULL, user_stream); if (user_stream->stream == NULL) { printf("xqc_stream_create error\n"); continue; @@ -4855,6 +4920,7 @@ int main(int argc, char *argv[]) { } } +skip_data: event_base_dispatch(eb); diff --git a/tests/test_server.c b/tests/test_server.c index efd475134..faca4c431 100644 --- a/tests/test_server.c +++ b/tests/test_server.c @@ -136,6 +136,7 @@ int g_ipv6; int g_batch=0; int g_lb_cid_encryption_on = 0; int g_enable_multipath = 0; +// xqc_multipath_version_t g_multipath_version = XQC_MULTIPATH_05; int g_enable_reinjection = 0; int g_spec_local_addr = 0; int g_mpshell = 0; @@ -1630,11 +1631,17 @@ xqc_server_socket_read_handler(xqc_server_ctx_t *ctx) uint64_t recv_time = now(); for (int i = 0; i < retval; i++) { recv_sum += msgs[i].msg_len; - +#ifdef XQC_NO_PID_PACKET_PROCESS if (xqc_engine_packet_process(ctx->engine, iovecs[i].iov_base, msgs[i].msg_len, (struct sockaddr *) (&ctx->local_addr), ctx->local_addrlen, (struct sockaddr *) (&pa[i]), peer_addrlen, (xqc_msec_t)recv_time, NULL) != XQC_OK) +#else + if (xqc_engine_packet_process(ctx->engine, iovecs[i].iov_base, msgs[i].msg_len, + (struct sockaddr *) (&ctx->local_addr), ctx->local_addrlen, + (struct sockaddr *) (&pa[i]), peer_addrlen, + XQC_UNKNOWN_PATH_ID, (xqc_msec_t)recv_time, NULL) != XQC_OK) +#endif { printf("xqc_server_read_handler: packet process err\n"); return; @@ -1674,10 +1681,17 @@ xqc_server_socket_read_handler(xqc_server_ctx_t *ctx) /*printf("peer_ip: %s, peer_port: %d\n", inet_ntoa(ctx->peer_addr.sin_addr), ntohs(ctx->peer_addr.sin_port)); printf("local_ip: %s, local_port: %d\n", inet_ntoa(ctx->local_addr.sin_addr), ntohs(ctx->local_addr.sin_port));*/ +#ifdef XQC_NO_PID_PACKET_PROCESS ret = xqc_engine_packet_process(ctx->engine, packet_buf, recv_size, (struct sockaddr *) (&ctx->local_addr), ctx->local_addrlen, (struct sockaddr *) (&peer_addr), peer_addrlen, (xqc_msec_t) recv_time, NULL); +#else + ret = xqc_engine_packet_process(ctx->engine, packet_buf, recv_size, + (struct sockaddr *) (&ctx->local_addr), ctx->local_addrlen, + (struct sockaddr *) (&peer_addr), peer_addrlen, + XQC_UNKNOWN_PATH_ID, (xqc_msec_t) recv_time, NULL); +#endif if (ret != XQC_OK) { printf("xqc_server_read_handler: packet process err: %d\n", ret); @@ -2095,7 +2109,7 @@ int main(int argc, char *argv[]) { }; int ch = 0; - while ((ch = getopt_long(argc, argv, "a:p:ec:LCs:w:r:l:u:x:6bS:MR:o:K:EmQ:U:yH", long_opts, NULL)) != -1) { + while ((ch = getopt_long(argc, argv, "a:p:ec:Cs:w:r:l:u:x:6bS:MR:o:EK:mLQ:U:yH", long_opts, NULL)) != -1) { switch (ch) { case 'H': printf("option disable h3_ext\n"); @@ -2389,6 +2403,7 @@ int main(int argc, char *argv[]) { .copa_delta_base = g_copa_delta, }, .enable_multipath = g_enable_multipath, + // .multipath_version = g_multipath_version, .spurious_loss_detect_on = 0, .max_datagram_frame_size = g_max_dgram_size, // .datagram_force_retrans_on = 1, diff --git a/tests/unittest/xqc_engine_test.c b/tests/unittest/xqc_engine_test.c index 8143e07ff..003354d86 100644 --- a/tests/unittest/xqc_engine_test.c +++ b/tests/unittest/xqc_engine_test.c @@ -45,11 +45,19 @@ xqc_test_engine_packet_process() xqc_msec_t recv_time = xqc_monotonic_timestamp(); +#ifdef XQC_NO_PID_PACKET_PROCESS xqc_int_t rc = xqc_engine_packet_process(engine, XQC_TEST_LONG_HEADER_PACKET_B, sizeof(XQC_TEST_LONG_HEADER_PACKET_B) - 1, (struct sockaddr *)(&local_addr), local_addrlen, (struct sockaddr *)(&peer_addr), peer_addrlen, recv_time, NULL); +#else + xqc_int_t rc = xqc_engine_packet_process(engine, XQC_TEST_LONG_HEADER_PACKET_B, + sizeof(XQC_TEST_LONG_HEADER_PACKET_B) - 1, + (struct sockaddr *)(&local_addr), local_addrlen, + (struct sockaddr *)(&peer_addr), peer_addrlen, + XQC_UNKNOWN_PATH_ID, recv_time, NULL); +#endif //CU_ASSERT(rc == XQC_OK); /* get connection */ @@ -68,10 +76,17 @@ xqc_test_engine_packet_process() conn->conn_flag |= XQC_CONN_FLAG_HANDSHAKE_COMPLETED; recv_time = xqc_monotonic_timestamp(); +#ifdef XQC_NO_PID_PACKET_PROCESS rc = xqc_engine_packet_process(engine, XQC_TEST_SHORT_HEADER_PACKET_A, sizeof(XQC_TEST_SHORT_HEADER_PACKET_A) - 1, (struct sockaddr *)&local_addr, local_addrlen, (struct sockaddr *)&peer_addr, peer_addrlen, recv_time, NULL); +#else + rc = xqc_engine_packet_process(engine, XQC_TEST_SHORT_HEADER_PACKET_A, + sizeof(XQC_TEST_SHORT_HEADER_PACKET_A) - 1, + (struct sockaddr *)&local_addr, local_addrlen, + (struct sockaddr *)&peer_addr, peer_addrlen, XQC_UNKNOWN_PATH_ID, recv_time, NULL); +#endif //CU_ASSERT(rc == XQC_OK); xqc_engine_destroy(engine); diff --git a/tests/unittest/xqc_h3_test.c b/tests/unittest/xqc_h3_test.c index 0675253e3..74f48f8ee 100644 --- a/tests/unittest/xqc_h3_test.c +++ b/tests/unittest/xqc_h3_test.c @@ -430,7 +430,7 @@ xqc_test_stream() xqc_connection_t *conn = test_engine_connect(); CU_ASSERT(conn != NULL); - xqc_stream_t *stream = xqc_create_stream_with_conn(conn, XQC_UNDEFINE_STREAM_ID, XQC_CLI_UNI, NULL); + xqc_stream_t *stream = xqc_create_stream_with_conn(conn, XQC_UNDEFINE_STREAM_ID, XQC_CLI_UNI, NULL, NULL); CU_ASSERT(stream != NULL); xqc_h3_conn_t *h3c = xqc_h3_conn_create(conn, NULL); diff --git a/xqc_configure.h.in b/xqc_configure.h.in index 93347d033..05d7691cf 100644 --- a/xqc_configure.h.in +++ b/xqc_configure.h.in @@ -6,4 +6,5 @@ #cmakedefine XQC_ENABLE_RENO #cmakedefine XQC_ENABLE_COPA #cmakedefine XQC_ENABLE_UNLIMITED -#cmakedefine XQC_ENABLE_MP_INTEROP \ No newline at end of file +#cmakedefine XQC_ENABLE_MP_INTEROP +#cmakedefine XQC_NO_PID_PACKET_PROCESS \ No newline at end of file