src/transport/xqc_packet_out.c (1,502 lines of code) (raw):

/** * @copyright Copyright (c) 2022, Alibaba Group Holding Limited */ #include "src/common/utils/vint/xqc_variable_len_int.h" #include "src/transport/xqc_packet_out.h" #include "src/transport/xqc_conn.h" #include "src/common/xqc_memory_pool.h" #include "src/transport/xqc_send_ctl.h" #include "src/transport/xqc_frame_parser.h" #include "src/transport/xqc_packet_parser.h" #include "src/transport/xqc_stream.h" #include "src/transport/xqc_utils.h" #include "src/transport/xqc_engine.h" #include "src/transport/xqc_multipath.h" #include "src/transport/xqc_datagram.h" #include "src/transport/xqc_reinjection.h" #include "src/transport/xqc_packet_out.h" #include "src/transport/xqc_cid.h" xqc_packet_out_t * xqc_packet_out_create(size_t po_buf_size) { xqc_packet_out_t *packet_out; packet_out = xqc_calloc(1, sizeof(xqc_packet_out_t)); if (!packet_out) { goto error; } packet_out->po_buf = xqc_malloc(XQC_PACKET_OUT_BUF_CAP); if (!packet_out->po_buf) { goto error; } packet_out->po_buf_cap = XQC_PACKET_OUT_BUF_CAP; packet_out->po_buf_size = po_buf_size; return packet_out; error: if (packet_out) { xqc_free(packet_out->po_buf); xqc_free(packet_out); } return NULL; } xqc_bool_t xqc_packet_out_on_specific_path(xqc_connection_t *conn, xqc_packet_out_t *po, xqc_path_ctx_t **path) { xqc_bool_t ret = XQC_FALSE; if (po->po_path_flag) { if (po->po_path_flag == XQC_PATH_SPECIFIED_BY_ACK && conn->conn_settings.mp_ack_on_any_path && po->po_frame_types & XQC_FRAME_BIT_ACK_MP) { /* * if the path is specified only due to ACK_MP, * we force the packet to be re-scheduled. */ po->po_path_flag &= ~XQC_PATH_SPECIFIED_BY_ACK; *path = NULL; return XQC_FALSE; } *path = xqc_conn_find_path_by_path_id(conn, po->po_path_id); /* no packets can be sent on a closing/closed/frozen path */ if ((*path == NULL) || ((*path)->path_state >= XQC_PATH_STATE_CLOSING) || (*path)->app_path_status == XQC_APP_PATH_STATUS_FROZEN) { po->po_path_flag &= ~(XQC_PATH_SPECIFIED_BY_ACK | XQC_PATH_SPECIFIED_BY_PTO | XQC_PATH_SPECIFIED_BY_REINJ | XQC_PATH_SPECIFIED_BY_FEC); if (po->po_path_flag) { if ((*path == NULL) || ((*path)->path_state >= XQC_PATH_STATE_CLOSING)) { /* if the packet can not be rescheduled and the path is closed, we remove it. */ xqc_send_queue_remove_send(&po->po_list); xqc_send_queue_insert_free(po, &conn->conn_send_queue->sndq_free_packets, conn->conn_send_queue); } ret = XQC_TRUE; } *path = NULL; } else { ret = XQC_TRUE; } } return ret; } xqc_bool_t xqc_packet_out_can_attach_ack(xqc_packet_out_t *po, xqc_path_ctx_t *path, xqc_pkt_type_t pkt_type) { if (po->po_pkt.pkt_type != pkt_type) { return XQC_FALSE; } if (po->po_frame_types & (XQC_FRAME_BIT_ACK | XQC_FRAME_BIT_ACK_MP | XQC_FRAME_BIT_REPAIR_SYMBOL)) { return XQC_FALSE; } if (path->path_flag && path->path_id != po->po_path_id) { return XQC_FALSE; } if (po->po_flag & XQC_POF_STREAM_NO_LEN) { return XQC_FALSE; } return XQC_TRUE; } xqc_bool_t xqc_packet_out_can_pto_probe(xqc_packet_out_t *po, uint64_t path_id) { if ((po->po_path_flag & (XQC_PATH_SPECIFIED_BY_PCPR | XQC_PATH_SPECIFIED_BY_REINJ | XQC_PATH_SPECIFIED_BY_PTMUD)) && path_id != po->po_path_id) { return XQC_FALSE; } return XQC_TRUE; } void xqc_packet_out_remove_ack_frame(xqc_packet_out_t *po) { if (po->po_frame_types & XQC_FRAME_BIT_ACK || po->po_frame_types & XQC_FRAME_BIT_ACK_MP) { po->po_used_size = po->po_ack_offset; po->po_frame_types &= ~(XQC_FRAME_BIT_ACK | XQC_FRAME_BIT_ACK_MP); po->po_path_flag &= ~(XQC_PATH_SPECIFIED_BY_ACK); } } void xqc_packet_out_copy(xqc_packet_out_t *dst, xqc_packet_out_t *src) { unsigned char *po_buf = dst->po_buf; size_t cap = dst->po_buf_cap; unsigned int size = dst->po_buf_size; xqc_memcpy(dst, src, sizeof(xqc_packet_out_t)); dst->po_origin_ref_cnt = 0; xqc_packet_out_t *origin = src->po_origin == NULL ? src : src->po_origin; /* pointers should carefully assigned in xqc_packet_out_copy */ dst->po_buf = po_buf; xqc_memcpy(dst->po_buf, src->po_buf, src->po_used_size); if (src->po_ppktno) { dst->po_ppktno = dst->po_buf + (src->po_ppktno - src->po_buf); } if (src->po_payload) { dst->po_payload = dst->po_buf + (src->po_payload - src->po_buf); } if (src->po_padding) { dst->po_padding = dst->po_buf + (src->po_padding - src->po_buf); } dst->po_origin = origin; origin->po_origin_ref_cnt++; dst->po_user_data = src->po_user_data; dst->po_path_id = src->po_path_id; dst->po_reserved_size = src->po_reserved_size; dst->po_flag &= ~XQC_POF_IN_UNACK_LIST; dst->po_flag &= ~XQC_POF_IN_PATH_BUF_LIST; dst->po_pr = src->po_pr; if (dst->po_pr) { dst->po_pr->ref_cnt++; } dst->po_send_cwnd_blk_ts = 0; dst->po_sched_cwnd_blk_ts = 0; dst->po_send_pacing_blk_ts = 0; } xqc_packet_out_t * xqc_packet_out_get(xqc_send_queue_t *send_queue) { xqc_packet_out_t *packet_out; unsigned int buf_size; size_t buf_cap, reserved_size; xqc_list_head_t *pos, *next; xqc_list_for_each_safe(pos, next, &send_queue->sndq_free_packets) { packet_out = xqc_list_entry(pos, xqc_packet_out_t, po_list); xqc_send_queue_remove_free(pos, send_queue); unsigned char *tmp = packet_out->po_buf; buf_size = send_queue->sndq_conn->pkt_out_size; buf_cap = packet_out->po_buf_cap; memset(packet_out, 0, sizeof(xqc_packet_out_t)); packet_out->po_buf = tmp; packet_out->po_buf_size = buf_size; packet_out->po_buf_cap = buf_cap; goto return_po; } packet_out = xqc_packet_out_create(send_queue->sndq_conn->pkt_out_size); if (!packet_out) { return NULL; } return_po: reserved_size = 0; if (send_queue->sndq_conn->conn_settings.fec_params.fec_encoder_scheme) { reserved_size += XQC_FEC_SPACE; } packet_out->po_reserved_size = reserved_size; return packet_out; } xqc_packet_out_t * xqc_packet_out_get_and_insert_send(xqc_send_queue_t *send_queue, enum xqc_pkt_type pkt_type) { xqc_packet_out_t *packet_out; packet_out = xqc_packet_out_get(send_queue); if (!packet_out) { return NULL; } packet_out->po_pkt.pkt_type = pkt_type; packet_out->po_pkt.pkt_pns = xqc_packet_type_to_pns(pkt_type); /* generate packet number when send */ packet_out->po_pkt.pkt_num = 0; xqc_send_queue_insert_send(packet_out, &send_queue->sndq_send_packets, send_queue); return packet_out; } void xqc_packet_out_destroy(xqc_packet_out_t *packet_out) { xqc_free(packet_out->po_buf); xqc_free(packet_out); } void xqc_maybe_recycle_packet_out(xqc_packet_out_t *packet_out, xqc_connection_t *conn) { /* recycle packetout if no frame in it */ if (packet_out->po_frame_types == 0) { xqc_list_del_init(&packet_out->po_list); xqc_send_queue_insert_free(packet_out, &conn->conn_send_queue->sndq_free_packets, conn->conn_send_queue); } } int xqc_write_packet_header(xqc_connection_t *conn, xqc_packet_out_t *packet_out) { if (packet_out->po_used_size > 0) { return XQC_OK; } ssize_t ret = XQC_OK; xqc_pkt_type_t pkt_type = packet_out->po_pkt.pkt_type; if (pkt_type == XQC_PTYPE_SHORT_HEADER && packet_out->po_used_size == 0) { ret = xqc_gen_short_packet_header(packet_out, conn->dcid_set.current_dcid.cid_buf, conn->dcid_set.current_dcid.cid_len, XQC_PKTNO_BITS, packet_out->po_pkt.pkt_num, conn->key_update_ctx.cur_out_key_phase); } else if (pkt_type != XQC_PTYPE_SHORT_HEADER && packet_out->po_used_size == 0) { ret = xqc_gen_long_packet_header(packet_out, conn->dcid_set.current_dcid.cid_buf, conn->dcid_set.current_dcid.cid_len, conn->scid_set.user_scid.cid_buf, conn->scid_set.user_scid.cid_len, conn->conn_token, conn->conn_token_len, conn->version, XQC_PKTNO_BITS); } if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|gen header error|%z|", ret); return ret; } packet_out->po_used_size += ret; return XQC_OK; } xqc_packet_out_t * xqc_write_new_packet(xqc_connection_t *conn, xqc_pkt_type_t pkt_type) { int ret; xqc_packet_out_t *packet_out; if (pkt_type == XQC_PTYPE_NUM) { pkt_type = xqc_state_to_pkt_type(conn); } packet_out = xqc_packet_out_get_and_insert_send(conn->conn_send_queue, pkt_type); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_packet_out_get_and_insert_send error|"); return NULL; } packet_out->po_path_id = XQC_INITIAL_PATH_ID; if (packet_out->po_used_size == 0) { ret = xqc_write_packet_header(conn, packet_out); if (ret) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_packet_header error|"); goto error; } } return packet_out; error: xqc_maybe_recycle_packet_out(packet_out, conn); return NULL; } xqc_packet_out_t * xqc_write_packet(xqc_connection_t *conn, xqc_pkt_type_t pkt_type, unsigned need) { int ret; xqc_packet_out_t *packet_out; if (pkt_type == XQC_PTYPE_NUM) { pkt_type = xqc_state_to_pkt_type(conn); } packet_out = xqc_send_queue_get_packet_out(conn->conn_send_queue, need, pkt_type); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_send_queue_get_packet_out error|"); return NULL; } packet_out->po_path_id = XQC_INITIAL_PATH_ID; if (packet_out->po_used_size == 0) { ret = xqc_write_packet_header(conn, packet_out); if (ret) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_packet_header error|"); goto error; } } return packet_out; error: xqc_maybe_recycle_packet_out(packet_out, conn); return NULL; } xqc_packet_out_t * xqc_write_packet_for_stream(xqc_connection_t *conn, xqc_pkt_type_t pkt_type, unsigned need, xqc_stream_t *stream) { int ret; xqc_packet_out_t *packet_out; if (pkt_type == XQC_PTYPE_NUM) { pkt_type = xqc_state_to_pkt_type(conn); } packet_out = xqc_send_queue_get_packet_out_for_stream(conn->conn_send_queue, need, pkt_type, stream); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_send_queue_get_packet_out_for_stream error|"); return NULL; } packet_out->po_path_id = XQC_INITIAL_PATH_ID; if (packet_out->po_used_size == 0) { ret = xqc_write_packet_header(conn, packet_out); if (ret) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_packet_header error|"); goto error; } } return packet_out; error: xqc_maybe_recycle_packet_out(packet_out, conn); return NULL; } int xqc_write_ack_to_one_packet(xqc_connection_t *conn, xqc_packet_out_t *packet_out, xqc_pkt_num_space_t pns) { ssize_t ret; int has_gap; xqc_packet_number_t largest_ack; xqc_usec_t now = xqc_monotonic_timestamp(); xqc_path_ctx_t *path = conn->conn_initial_path; xqc_pn_ctl_t *pn_ctl = xqc_get_pn_ctl(conn, path); ret = xqc_gen_ack_frame(conn, packet_out, now, conn->local_settings.ack_delay_exponent, &pn_ctl->ctl_recv_record[pns], path->path_send_ctl->ctl_largest_recv_time[pns], &has_gap, &largest_ack); if (ret < 0) { goto error; } xqc_log(conn->log, XQC_LOG_DEBUG, "|ack_size:%ui|path:%ui|path_largest_recv:%ui|frame_largest_recv:%ui|", ret, path->path_id, path->path_send_ctl->ctl_largest_received[pns], xqc_recv_record_largest(&pn_ctl->ctl_recv_record[pns])); packet_out->po_ack_offset = packet_out->po_used_size; packet_out->po_used_size += ret; packet_out->po_largest_ack = largest_ack; packet_out->po_path_flag |= XQC_PATH_SPECIFIED_BY_ACK; packet_out->po_path_id = path->path_id; path->path_send_ctl->ctl_ack_eliciting_pkt[pns] = 0; if (has_gap) { conn->conn_flag |= XQC_CONN_FLAG_ACK_HAS_GAP; } else { conn->conn_flag &= ~XQC_CONN_FLAG_ACK_HAS_GAP; } path->path_flag &= ~(XQC_PATH_FLAG_SHOULD_ACK_INIT << pns); conn->ack_flag &= ~(1 << (pns + path->path_id * XQC_PNS_N)); path->path_send_ctl->ctl_ack_sent_cnt++; return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return ret; } #ifdef XQC_ENABLE_FEC xqc_int_t xqc_write_sid_frame_to_one_packet(xqc_connection_t *conn, xqc_packet_out_t *packet_out) { ssize_t ret; ret = xqc_gen_sid_frame(conn, packet_out); if (ret == -XQC_EFEC_TOLERABLE_ERROR) { return ret; } else if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|quic_fec|xqc_gen_sid_frame error|"); return -XQC_EWRITE_PKT; } packet_out->po_used_size += ret; packet_out->po_reserved_size -= ret; return XQC_OK; } xqc_packet_out_t * xqc_write_one_repair_packet(xqc_connection_t *conn, xqc_int_t fss_esi, xqc_int_t repair_idx, uint8_t bm_idx) { uint64_t path_id; xqc_int_t ret; xqc_packet_out_t *packet_out; xqc_send_queue_t *send_queue = conn->conn_send_queue; packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|quic_fec|xqc_write_new_packet error|"); return NULL; } ret = xqc_gen_repair_frame(conn, packet_out, fss_esi, repair_idx, bm_idx); if (ret < 0) { xqc_log(conn->log, XQC_LOG_WARN, "|quic_fec|xqc_gen_repair_frame error|"); xqc_send_queue_remove_send(&packet_out->po_list); xqc_send_queue_insert_free(packet_out, &send_queue->sndq_free_packets, send_queue); return NULL; } conn->fec_ctl->fec_send_repair_num_total++; return packet_out; } xqc_int_t xqc_write_repair_packets(xqc_connection_t *conn, xqc_int_t fss_esi, xqc_list_head_t *prev, xqc_int_t repair_packet_num, uint8_t bm_idx) { xqc_int_t ret, curr_repair_idx, insert_flag; xqc_path_ctx_t *path; xqc_list_head_t *head; xqc_packet_out_t *packet_out; curr_repair_idx = insert_flag = 0; if (repair_packet_num <= 0) { xqc_log(conn->log, XQC_LOG_WARN, "|current code rate is too low to generate repair packets."); return XQC_OK; } for (xqc_int_t i = 0; i < repair_packet_num; i++) { packet_out = xqc_write_one_repair_packet(conn, fss_esi, curr_repair_idx, bm_idx); if (packet_out == NULL) { continue; } curr_repair_idx++; // move to the next position of current src symbol xqc_send_queue_move_to_head(&packet_out->po_list, prev); prev = &packet_out->po_list; insert_flag = 1; } if (insert_flag) { return XQC_OK; } xqc_log(conn->log, XQC_LOG_ERROR, "|quic_fec| no repair packets inserted."); return -XQC_EFEC_SCHEME_ERROR; } #endif xqc_int_t xqc_write_ack_or_mp_ack_or_ext_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, xqc_bool_t is_ext_ack, xqc_bool_t is_new_pkt) { int ret; if (is_mp_ack) { ret = xqc_write_ack_mp_to_one_packet(conn, path, packet_out, pns); } else { if (is_ext_ack) { ret = xqc_write_ack_ext_to_one_packet(conn, packet_out, pns, is_new_pkt); } else { ret = xqc_write_ack_to_one_packet(conn, packet_out, pns); } } return ret; } xqc_int_t xqc_write_ack_or_mp_ack_or_ext_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 */ xqc_bool_t is_ext_ack = 0; int ret = XQC_OK; xqc_path_ctx_t *path; xqc_list_head_t *path_pos, *path_next; xqc_list_for_each_safe(path_pos, path_next, &conn->conn_paths_list) { path = xqc_list_entry(path_pos, xqc_path_ctx_t, path_list); if (path->path_state < XQC_PATH_STATE_VALIDATING || path->path_state >= XQC_PATH_STATE_CLOSED) { continue; } for (pns = 0; pns < XQC_PNS_N; ++pns) { /* If there's no such pns in the connection, continue the loop */ if (!(path->path_flag & (XQC_PATH_FLAG_SHOULD_ACK_INIT << pns))) { continue; } /* 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; } 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_MP_ENABLED) { is_mp_ack = 1; } else if ((conn->conn_settings.extended_ack_features & XQC_ACK_EXT_FEATURE_BIT_RECV_TS) && pns == XQC_PNS_APP_DATA && xqc_recv_timestamps_info_length(path->recv_ts_info) > 0) { /* ENC count is not supported */ is_ext_ack = 1; } path_buffer: /* 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_or_ext_ack_to_one_packet(conn, packet_out, pns, path, is_mp_ack, is_ext_ack, 0); if (ret == -XQC_ENOBUF) { xqc_log(conn->log, XQC_LOG_DEBUG, "|xqc_write_ack_or_mp_ack_or_ext_ack_to_one_packet try new packet|"); goto write_new; } else if (ret == XQC_OK) { goto done; } else { return ret; } } goto write_new; } conn_buffer: 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)) { if (is_ext_ack) { ret = xqc_write_ack_ext_to_one_packet(conn, packet_out, pns, 0); } else { 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; } } 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_or_mp_ack_or_ext_ack_to_one_packet(conn, packet_out, pns, path, is_mp_ack, is_ext_ack, 1); if (ret != XQC_OK) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_ack_or_mp_ack_or_ext_ack_to_one_packet write to new packet error|ret:%d|is_mp_ack:%d|is_ext_ack:%d|", ret, is_mp_ack, is_ext_ack); 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_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) { ssize_t ret; xqc_packet_out_t *packet_out; 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; } ret = xqc_gen_ping_frame(packet_out); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_ping_frame error|"); goto error; } packet_out->po_user_data = po_user_data; packet_out->po_used_size += ret; /* * xquic supports inner PING and user PING, user PING shall be notified * to upper level while inner PING shall not. if XQC_POF_NOTIFY is not set, * it's an inner PING, do no callback */ if (notify) { packet_out->po_flag |= XQC_POF_NOTIFY; if (pr) { packet_out->po_pr = pr; pr->ref_cnt++; } } if (path) { packet_out->po_path_id = path->path_id; packet_out->po_path_flag |= XQC_PATH_SPECIFIED_BY_KAP; } conn->conn_flag &= ~XQC_CONN_FLAG_PING; xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return ret; } int xqc_write_pmtud_ping_to_packet(xqc_path_ctx_t *path, size_t probing_size, xqc_pkt_type_t pkt_type) { int ret; xqc_packet_out_t *packet_out; xqc_connection_t *conn = path->parent_conn; 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; } packet_out->po_buf_size = probing_size; if (packet_out->po_buf_size > packet_out->po_buf_cap || packet_out->po_buf_size < packet_out->po_used_size) { xqc_log(conn->log, XQC_LOG_ERROR, "|invalid PMTUD probing size|"); ret = -XQC_EPMTUD_PROBING_SIZE; goto error; } ret = xqc_gen_ping_frame(packet_out); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_ping_frame error|"); goto error; } packet_out->po_used_size += ret; packet_out->po_path_id = path->path_id; packet_out->po_path_flag |= XQC_PATH_SPECIFIED_BY_PTMUD; packet_out->po_flag |= XQC_POF_PMTUD_PROBING; packet_out->po_max_pkt_out_size = conn->max_pkt_out_size; xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return ret; } int xqc_write_conn_close_to_packet(xqc_connection_t *conn, uint64_t err_code) { ssize_t ret; xqc_packet_out_t *packet_out; xqc_pkt_type_t pkt_type = XQC_PTYPE_INIT; /* select packet type */ if (xqc_tls_is_key_ready(conn->tls, XQC_ENC_LEV_HSK, XQC_KEY_TYPE_TX_WRITE)) { pkt_type = XQC_PTYPE_HSK; } /* peer may not have received the handshake packet */ if ((conn->conn_flag & XQC_CONN_FLAG_HANDSHAKE_COMPLETED && conn->conn_flag & XQC_CONN_FLAG_HSK_ACKED) || (conn->conn_flag & XQC_CONN_FLAG_HANDSHAKE_CONFIRMED)) { pkt_type = 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; } ret = xqc_gen_conn_close_frame(packet_out, err_code, err_code >= H3_NO_ERROR ? 1:0, 0); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_conn_close_frame error|"); goto error; } packet_out->po_used_size += ret; xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return ret; } int xqc_write_reset_stream_to_packet(xqc_connection_t *conn, xqc_stream_t *stream, uint64_t err_code, uint64_t final_size) { ssize_t ret; xqc_packet_out_t *packet_out; xqc_pkt_type_t pkt_type = XQC_PTYPE_SHORT_HEADER; int support_0rtt = xqc_conn_is_ready_to_send_early_data(conn); xqc_bool_t buff_reset = XQC_FALSE; if (!(conn->conn_flag & XQC_CONN_FLAG_CAN_SEND_1RTT)) { if ((conn->conn_type == XQC_CONN_TYPE_CLIENT) && (conn->conn_state == XQC_CONN_STATE_CLIENT_INITIAL_SENT) && support_0rtt) { pkt_type = XQC_PTYPE_0RTT; conn->conn_flag |= XQC_CONN_FLAG_HAS_0RTT; stream->stream_flag |= XQC_STREAM_FLAG_HAS_0RTT; } else { buff_reset = XQC_TRUE; } } 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_gen_reset_stream_frame(packet_out, stream->stream_id, err_code, final_size); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_reset_stream_frame error|"); goto error; } stream->stream_err = err_code; packet_out->po_used_size += ret; /* new packet with index 0 */ packet_out->po_stream_frames[0].ps_stream_id = stream->stream_id; packet_out->po_stream_frames[0].ps_is_reset = 1; packet_out->po_stream_frames[0].ps_is_used = 1; packet_out->po_stream_frames_idx++; if (stream->stream_state_send < XQC_SEND_STREAM_ST_RESET_SENT) { xqc_stream_send_state_update(stream, XQC_SEND_STREAM_ST_RESET_SENT); } if (stream->stream_stats.app_reset_time == 0) { stream->stream_stats.app_reset_time = xqc_monotonic_timestamp(); } if (buff_reset) { xqc_conn_buff_1rtt_packet(conn, packet_out); } xqc_log(conn->log, XQC_LOG_DEBUG, "|stream_id:%ui|stream_state_send:%d|", stream->stream_id, stream->stream_state_send); return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return ret; } int xqc_write_stop_sending_to_packet(xqc_connection_t *conn, xqc_stream_t *stream, uint64_t err_code) { ssize_t ret; xqc_packet_out_t *packet_out; /* * A STOP_SENDING frame can be sent for streams in the Recv or Size Known states */ if (stream->stream_state_recv >= XQC_RECV_STREAM_ST_DATA_RECVD) { xqc_log(conn->log, XQC_LOG_WARN, "|beyond DATA_RECVD|stream_state_recv:%d|", stream->stream_state_recv); return XQC_OK; } xqc_pkt_type_t pkt_type = XQC_PTYPE_SHORT_HEADER; int support_0rtt = xqc_conn_is_ready_to_send_early_data(conn); xqc_bool_t buff_pkt = XQC_FALSE; if (!(conn->conn_flag & XQC_CONN_FLAG_CAN_SEND_1RTT)) { if ((conn->conn_type == XQC_CONN_TYPE_CLIENT) && (conn->conn_state == XQC_CONN_STATE_CLIENT_INITIAL_SENT) && support_0rtt) { pkt_type = XQC_PTYPE_0RTT; conn->conn_flag |= XQC_CONN_FLAG_HAS_0RTT; stream->stream_flag |= XQC_STREAM_FLAG_HAS_0RTT; } else { buff_pkt = XQC_TRUE; } } 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_gen_stop_sending_frame(packet_out, stream->stream_id, err_code); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_stop_sending_frame error|"); goto error; } packet_out->po_used_size += ret; if (buff_pkt) { xqc_conn_buff_1rtt_packet(conn, packet_out); } return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return -XQC_EWRITE_PKT; } int xqc_write_data_blocked_to_packet(xqc_connection_t *conn, uint64_t data_limit) { ssize_t ret; xqc_packet_out_t *packet_out; packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); return -XQC_EWRITE_PKT; } ret = xqc_gen_data_blocked_frame(packet_out, data_limit); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_data_blocked_frame error|"); goto error; } packet_out->po_used_size += ret; /* we need to send this packet asap */ xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return -XQC_EWRITE_PKT; } int xqc_write_stream_data_blocked_to_packet(xqc_connection_t *conn, xqc_stream_id_t stream_id, uint64_t stream_data_limit) { ssize_t ret; xqc_packet_out_t *packet_out; packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); return -XQC_EWRITE_PKT; } ret = xqc_gen_stream_data_blocked_frame(packet_out, stream_id, stream_data_limit); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_stream_data_blocked_frame error|"); goto error; } packet_out->po_used_size += ret; /* we need to send this packet asap */ xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return -XQC_EWRITE_PKT; } int xqc_write_streams_blocked_to_packet(xqc_connection_t *conn, uint64_t stream_limit, int bidirectional) { ssize_t ret; xqc_packet_out_t *packet_out; 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; } ret = xqc_gen_streams_blocked_frame(packet_out, stream_limit, bidirectional); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_streams_blocked_frame error|"); goto error; } packet_out->po_used_size += ret; xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return -XQC_EWRITE_PKT; } int xqc_write_max_data_to_packet(xqc_connection_t *conn, uint64_t max_data) { ssize_t ret; xqc_packet_out_t *packet_out; packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); return -XQC_EWRITE_PKT; } ret = xqc_gen_max_data_frame(packet_out, max_data); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_max_data_frame error|"); goto error; } packet_out->po_used_size += ret; xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return -XQC_EWRITE_PKT; } 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 pkt_type) { ssize_t ret = XQC_OK; xqc_packet_out_t *packet_out; 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_gen_max_stream_data_frame(packet_out, stream_id, max_stream_data); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_max_stream_data_frame error|"); goto error; } packet_out->po_used_size += ret; xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return -XQC_EWRITE_PKT; } int xqc_write_max_streams_to_packet(xqc_connection_t *conn, uint64_t max_stream, int bidirectional) { ssize_t ret = XQC_ERROR; xqc_packet_out_t *packet_out; if (max_stream > XQC_MAX_STREAMS) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_max_streams_to_packet error|set max_stream:%ui", max_stream); return -XQC_EPARAM; } 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; } ret = xqc_gen_max_streams_frame(packet_out, max_stream, bidirectional); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_max_streams_frame error|"); goto error; } packet_out->po_used_size += ret; xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); xqc_log(conn->log, XQC_LOG_DEBUG, "|new_max_stream:%ui|", max_stream); return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return -XQC_EWRITE_PKT; } int xqc_write_new_token_to_packet(xqc_connection_t *conn) { ssize_t ret = 0; unsigned need; xqc_packet_out_t *packet_out; unsigned char token[XQC_MAX_TOKEN_LEN]; unsigned token_len = XQC_MAX_TOKEN_LEN; xqc_conn_gen_token(conn, token, &token_len); need = 1 /* type */ + xqc_vint_get_2bit(token_len) /* token len */ + token_len; /* token */ packet_out = xqc_write_packet(conn, XQC_PTYPE_SHORT_HEADER, need); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); return -XQC_EWRITE_PKT; } ret = xqc_gen_new_token_frame(packet_out, token, token_len); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_new_token_frame error|"); goto error; } packet_out->po_used_size += ret; xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return ret; } void xqc_fec_encode_queue_insert_send(xqc_packet_out_t *po, xqc_stream_t *stream) { // xqc_list_add_tail(&po->po_list, &stream->stream_fec_ctl.stream_fec_send_packets); if (stream->stream_fec_ctl.stream_fec_head == NULL) { stream->stream_fec_ctl.stream_fec_head = po->po_list.prev; } stream->stream_fec_ctl.stream_fec_tail = &po->po_list; // count encode list number stream->stream_fec_ctl.stream_fec_syb_num++; } 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) { 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; xqc_int_t ret; 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; packet_out = xqc_write_packet_for_stream(conn, pkt_type, need, stream); if (packet_out == NULL) { return -XQC_EWRITE_PKT; } n_written = xqc_gen_stream_frame(packet_out, stream->stream_id, stream->stream_send_offset, fin, payload, payload_size, send_data_written); if (n_written < 0) { xqc_maybe_recycle_packet_out(packet_out, conn); return n_written; } stream->stream_send_offset += *send_data_written; stream->stream_conn->conn_flow_ctl.fc_data_sent += *send_data_written; packet_out->po_used_size += n_written; packet_out->po_stream_id = stream->stream_id; packet_out->po_stream_offset = stream->stream_send_offset; #ifdef XQC_ENABLE_FEC // FEC process source packet on stream level if (conn->conn_settings.fec_level == XQC_FEC_STREAM_LEVEL) { // if current stream requires fec encode, save it to encode_list if (stream->stream_fec_ctl.enable_fec && conn->conn_settings.fec_params.fec_encoder_scheme == XQC_PACKET_MASK_CODE && !(packet_out->po_frame_types & XQC_FRAME_BIT_SID || packet_out->po_frame_types & XQC_FRAME_BIT_REPAIR_SYMBOL)) { xqc_fec_encode_queue_insert_send(packet_out, stream); } } else if (stream->stream_fec_blk_mode != XQC_SLIM_SIZE_REQ && conn->conn_settings.fec_params.fec_encoder_scheme) { // FEC tag source packet to be processed on connection level packet_out->po_flag |= XQC_POF_USE_FEC; if (conn->conn_settings.fec_params.fec_encoder_scheme == XQC_PACKET_MASK_CODE) { packet_out->po_stream_fec_blk_mode = stream->stream_fec_blk_mode; } } #endif 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(); } if (!stream->stream_stats.first_write_time) { stream->stream_stats.first_write_time = xqc_monotonic_timestamp(); } return XQC_OK; } int xqc_write_datagram_frame_to_packet(xqc_connection_t *conn, xqc_pkt_type_t pkt_type, const unsigned char *data, size_t data_len, uint64_t *dgram_id, xqc_bool_t use_supplied_dgram_id, xqc_data_qos_level_t qos_level) { xqc_packet_out_t *packet_out; packet_out = xqc_write_new_packet(conn, pkt_type); if (packet_out == NULL) { return -XQC_EWRITE_PKT; } int ret; ret = xqc_gen_datagram_frame(packet_out, data, data_len); if (ret < 0) { xqc_maybe_recycle_packet_out(packet_out, conn); return ret; } if (use_supplied_dgram_id) { packet_out->po_dgram_id = *dgram_id; } else { packet_out->po_dgram_id = conn->next_dgram_id++; } if (dgram_id) { *dgram_id = packet_out->po_dgram_id; } if (pkt_type == XQC_PTYPE_0RTT) { conn->zero_rtt_count++; } if (qos_level > XQC_DATA_QOS_HIGH) { if (qos_level == XQC_DATA_QOS_PROBING) { /* must reinject the packet on a different path */ packet_out->po_flag |= XQC_POF_REINJECT_DIFF_PATH; packet_out->po_flag |= XQC_POF_QOS_PROBING; } else { packet_out->po_flag |= XQC_POF_USE_FEC; packet_out->po_flag |= XQC_POF_NOT_REINJECT; } } else { packet_out->po_flag |= XQC_POF_QOS_HIGH; } return XQC_OK; } /* [Transport] 12.4, HANDSHAKE_DONE only send in 1-RTT packet */ int xqc_write_handshake_done_frame_to_packet(xqc_connection_t *conn) { ssize_t n_written = 0; xqc_packet_out_t *packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); if (packet_out == NULL) { return -XQC_EWRITE_PKT; } n_written = xqc_gen_handshake_done_frame(packet_out); if (n_written < 0) { xqc_maybe_recycle_packet_out(packet_out, conn); return n_written; } packet_out->po_used_size += n_written; xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); return XQC_OK; } xqc_int_t xqc_write_new_conn_id_frame_to_packet(xqc_connection_t *conn, uint64_t retire_prior_to) { xqc_int_t ret = XQC_ERROR; xqc_packet_out_t *packet_out = NULL; xqc_cid_t new_conn_cid; uint8_t sr_token[XQC_STATELESS_RESET_TOKENLEN]; xqc_cid_set_inner_t *inner_set; inner_set = xqc_get_path_cid_set(&conn->scid_set, XQC_INITIAL_PATH_ID); if (!inner_set) { return -XQC_EGENERATE_CID; } /* only reserve bits for server side */ inner_set->largest_scid_seq_num++; if (XQC_OK != xqc_generate_cid(conn->engine, &conn->scid_set.user_scid, &new_conn_cid, inner_set->largest_scid_seq_num)) { xqc_log(conn->log, XQC_LOG_WARN, "|generate cid error|"); return -XQC_EGENERATE_CID; } /* generate stateless reset token */ xqc_gen_reset_token(&new_conn_cid, sr_token, XQC_STATELESS_RESET_TOKENLEN, conn->engine->config->reset_token_key, conn->engine->config->reset_token_keylen); /* insert to scid_set & add scid_unused_cnt */ ret = xqc_cid_set_insert_cid(&conn->scid_set, &new_conn_cid, XQC_CID_UNUSED, conn->remote_settings.active_connection_id_limit, XQC_INITIAL_PATH_ID); if (ret != XQC_OK) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_cid_set_insert_cid error|limit:%ui|unused:%i|used:%i|", conn->remote_settings.active_connection_id_limit, xqc_cid_set_get_unused_cnt(&conn->scid_set, XQC_INITIAL_PATH_ID), xqc_cid_set_get_used_cnt(&conn->scid_set, XQC_INITIAL_PATH_ID)); return ret; } ret = xqc_insert_conns_hash(conn->engine->conns_hash, conn, new_conn_cid.cid_buf, new_conn_cid.cid_len); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|insert new_cid into conns_hash failed|"); return ret; } packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); return -XQC_EWRITE_PKT; } ret = xqc_gen_new_conn_id_frame(packet_out, &new_conn_cid, retire_prior_to, sr_token); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_new_conn_id_frame error|"); goto error; } packet_out->po_used_size += ret; packet_out->po_new_cid_seq = new_conn_cid.cid_seq_num; packet_out->po_new_cid_path = XQC_INITIAL_PATH_ID; xqc_log(conn->log, XQC_LOG_DEBUG, "|gen_new_scid|cid:%s|sr_token:%s|seq_num:%ui", xqc_scid_str(conn->engine, &new_conn_cid), xqc_sr_token_str(conn->engine, new_conn_cid.sr_token), new_conn_cid.cid_seq_num); xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return ret; } xqc_int_t xqc_write_retire_conn_id_frame_to_packet(xqc_connection_t *conn, uint64_t seq_num) { xqc_int_t ret = XQC_ERROR; xqc_path_ctx_t *path = conn->conn_initial_path; /* change path_dcid */ if (path->path_dcid.cid_seq_num == seq_num) { ret = xqc_get_unused_cid(&conn->dcid_set, &path->path_dcid, XQC_INITIAL_PATH_ID); if (ret != XQC_OK) { xqc_log(conn->log, XQC_LOG_ERROR, "|conn have no available dcid|"); return ret; } xqc_datagram_record_mss(conn); } /* replace conn current_dcid */ if (seq_num == conn->dcid_set.current_dcid.cid_seq_num && conn->dcid_set.current_dcid.path_id == XQC_INITIAL_PATH_ID) { xqc_cid_copy(&conn->dcid_set.current_dcid, &path->path_dcid); } xqc_log(conn->log, XQC_LOG_DEBUG, "|get_new_dcid:%s|seq_num:%ui|", xqc_dcid_str(conn->engine, &conn->dcid_set.current_dcid), conn->dcid_set.current_dcid.cid_seq_num); xqc_packet_out_t *packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); return -XQC_EWRITE_PKT; } ret = xqc_gen_retire_conn_id_frame(packet_out, seq_num); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_retire_conn_id_frame error|"); xqc_maybe_recycle_packet_out(packet_out, conn); return ret; } packet_out->po_used_size += ret; xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); return XQC_OK; } 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 ret = XQC_ERROR; xqc_packet_out_t *packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); return -XQC_EWRITE_PKT; } ret = xqc_gen_path_challenge_frame(packet_out, path->path_challenge_data); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_path_challenge_frame error|%d|", ret); goto error; } packet_out->po_used_size += ret; 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++; ret = xqc_gen_path_status_frame(conn, packet_out, path->path_id, path->app_path_status_send_seq_num, path->app_path_status); if (ret < 0) { /* ignore */ xqc_log(conn->log, XQC_LOG_ERROR, "|attach status 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(conn->engine, 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); return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return ret; } xqc_int_t xqc_write_path_response_frame_to_packet(xqc_connection_t *conn, xqc_path_ctx_t *path, unsigned char *path_response_data) { xqc_int_t ret = XQC_ERROR; xqc_packet_out_t *packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); return -XQC_EWRITE_PKT; } ret = xqc_gen_path_response_frame(packet_out, path_response_data); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_path_response_frame error|%d|", ret); goto error; } packet_out->po_used_size += ret; packet_out->po_path_flag |= XQC_PATH_SPECIFIED_BY_PCPR; packet_out->po_path_id = path->path_id; 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); return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return ret; } 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) { ssize_t ret; int has_gap; xqc_packet_number_t largest_ack; xqc_usec_t now = xqc_monotonic_timestamp(); 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, 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], &has_gap, &largest_ack); if (ret < 0) { goto error; } xqc_log(conn->log, XQC_LOG_DEBUG, "|path:%ui|ack_size:%ui|", path->path_id, ret); packet_out->po_ack_offset = packet_out->po_used_size; packet_out->po_used_size += ret; packet_out->po_largest_ack = largest_ack; packet_out->po_path_flag |= XQC_PATH_SPECIFIED_BY_ACK; packet_out->po_path_id = path->path_id; path->path_send_ctl->ctl_ack_eliciting_pkt[pns] = 0; if (has_gap) { conn->conn_flag |= XQC_CONN_FLAG_ACK_HAS_GAP; } else { conn->conn_flag &= ~XQC_CONN_FLAG_ACK_HAS_GAP; } path->path_flag &= ~(XQC_PATH_FLAG_SHOULD_ACK_INIT << pns); conn->ack_flag &= ~(1 << (pns + path->path_id * XQC_PNS_N)); path->path_send_ctl->ctl_ack_sent_cnt++; return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return ret; } xqc_int_t xqc_write_path_abandon_frame_to_packet(xqc_connection_t *conn, xqc_path_ctx_t *path) { xqc_int_t ret = XQC_ERROR; xqc_packet_out_t *packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); return -XQC_EWRITE_PKT; } uint64_t path_id = path->path_id; ret = xqc_gen_path_abandon_frame(conn, packet_out, path_id, 0); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_path_abandon_frame error|%d|", ret); goto error; } packet_out->po_used_size += ret; xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); xqc_log(conn->log, XQC_LOG_DEBUG, "|path:%ui|path_id:%ui|", path->path_id, path_id); return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return ret; } xqc_int_t xqc_write_path_status_frame_to_packet(xqc_connection_t *conn, xqc_path_ctx_t *path) { xqc_int_t ret = XQC_ERROR; xqc_packet_out_t *packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); return -XQC_EWRITE_PKT; } path->app_path_status_send_seq_num++; ret = xqc_gen_path_status_frame(conn, packet_out, path->path_id, path->app_path_status_send_seq_num, path->app_path_status); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|error|%d|", ret); goto error; } packet_out->po_used_size += ret; xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); xqc_log(conn->log, XQC_LOG_DEBUG, "|status=%d|", path->app_path_status); return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return ret; } size_t xqc_get_po_remained_size(xqc_packet_out_t *po) { size_t res; res = po->po_buf_size - po->po_used_size - po->po_reserved_size; return xqc_max(res, 0); } size_t xqc_get_po_remained_size_with_ack_spc(xqc_packet_out_t *po) { return xqc_get_po_remained_size(po) + XQC_ACK_SPACE; } xqc_int_t xqc_write_mp_new_conn_id_frame_to_packet(xqc_connection_t *conn, uint64_t retire_prior_to, uint64_t path_id) { xqc_int_t ret = XQC_ERROR; xqc_packet_out_t *packet_out = NULL; xqc_cid_t new_conn_cid; uint8_t sr_token[XQC_STATELESS_RESET_TOKENLEN]; xqc_cid_set_inner_t *inner_set; inner_set = xqc_get_path_cid_set(&conn->scid_set, path_id); if (!inner_set) { return -XQC_EGENERATE_CID; } if (path_id == XQC_INITIAL_PATH_ID || ((inner_set->unused_cnt + inner_set->used_cnt + inner_set->retired_cnt) > 0)) { inner_set->largest_scid_seq_num++; } if (XQC_OK != xqc_generate_cid(conn->engine, &conn->scid_set.user_scid, &new_conn_cid, inner_set->largest_scid_seq_num)) { xqc_log(conn->log, XQC_LOG_WARN, "|generate cid error|"); return -XQC_EGENERATE_CID; } /* generate stateless reset token */ xqc_gen_reset_token(&new_conn_cid, sr_token, XQC_STATELESS_RESET_TOKENLEN, conn->engine->config->reset_token_key, conn->engine->config->reset_token_keylen); /* insert to scid_set & add scid_unused_cnt */ ret = xqc_cid_set_insert_cid(&conn->scid_set, &new_conn_cid, XQC_CID_UNUSED, conn->remote_settings.active_connection_id_limit, path_id); if (ret != XQC_OK) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_cid_set_insert_cid error|limit:%ui|unused:%i|used:%i|", conn->remote_settings.active_connection_id_limit, xqc_cid_set_get_unused_cnt(&conn->scid_set, path_id), xqc_cid_set_get_used_cnt(&conn->scid_set, path_id)); return ret; } ret = xqc_insert_conns_hash(conn->engine->conns_hash, conn, new_conn_cid.cid_buf, new_conn_cid.cid_len); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|insert new_cid into conns_hash failed|"); return ret; } packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); return -XQC_EWRITE_PKT; } ret = xqc_gen_mp_new_conn_id_frame(packet_out, &new_conn_cid, retire_prior_to, sr_token, path_id); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_mp_new_conn_id_frame error|"); goto error; } packet_out->po_used_size += ret; packet_out->po_new_cid_seq = new_conn_cid.cid_seq_num; packet_out->po_new_cid_path = path_id; xqc_log(conn->log, XQC_LOG_DEBUG, "|path_id:%ui|cid:%s|sr_token:%s|seq_num:%ui", path_id, xqc_scid_str(conn->engine, &new_conn_cid), xqc_sr_token_str(conn->engine, new_conn_cid.sr_token), new_conn_cid.cid_seq_num); xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return ret; } xqc_int_t xqc_write_mp_retire_conn_id_frame_to_packet(xqc_connection_t *conn, uint64_t seq_num, uint64_t path_id) { xqc_int_t ret = XQC_ERROR; xqc_path_ctx_t *path; path = xqc_conn_find_path_by_path_id(conn, path_id); /* replace path's dcid */ if (path && path->path_dcid.cid_seq_num == seq_num) { ret = xqc_get_unused_cid(&conn->dcid_set, &path->path_dcid, path_id); if (ret != XQC_OK) { xqc_log(conn->log, XQC_LOG_ERROR, "|path:%ui|don't have available dcid|", path_id); return ret; } xqc_datagram_record_mss(conn); } /* select new current_dcid to replace the cid to be retired */ if (seq_num == conn->dcid_set.current_dcid.cid_seq_num && path_id == conn->dcid_set.current_dcid.path_id) { if (!path) { ret = xqc_get_unused_cid(&conn->dcid_set, &conn->dcid_set.current_dcid, path_id); if (ret != XQC_OK) { xqc_log(conn->log, XQC_LOG_ERROR, "|path:%ui|don't have available dcid|", path_id); return ret; } xqc_datagram_record_mss(conn); } else { xqc_cid_copy(&conn->dcid_set.current_dcid, &path->path_dcid); } } xqc_log(conn->log, XQC_LOG_DEBUG, "|get_new_dcid:%s|seq_num:%ui|path_id:%ui|", xqc_dcid_str(conn->engine, &conn->dcid_set.current_dcid), conn->dcid_set.current_dcid.cid_seq_num, path_id); xqc_packet_out_t *packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); return -XQC_EWRITE_PKT; } ret = xqc_gen_mp_retire_conn_id_frame(packet_out, seq_num, path_id); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_mp_retire_conn_id_frame error|"); xqc_maybe_recycle_packet_out(packet_out, conn); return ret; } packet_out->po_used_size += ret; xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); return XQC_OK; } int xqc_write_max_path_id_to_packet(xqc_connection_t *conn, uint64_t max_path_id) { ssize_t ret = XQC_ERROR; xqc_packet_out_t *packet_out; xqc_log(conn->log, XQC_LOG_DEBUG, "|set max_path_id:%ui|", max_path_id); packet_out = xqc_write_new_packet(conn, XQC_PTYPE_SHORT_HEADER); if (packet_out == NULL) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_write_new_packet error|"); return -XQC_EWRITE_PKT; } ret = xqc_gen_max_path_id_frame(packet_out, max_path_id); if (ret < 0) { xqc_log(conn->log, XQC_LOG_ERROR, "|xqc_gen_max_streams_frame error|"); goto error; } packet_out->po_used_size += ret; xqc_send_queue_move_to_high_pri(&packet_out->po_list, conn->conn_send_queue); xqc_log(conn->log, XQC_LOG_DEBUG, "|max_path_id:%ui|", max_path_id); return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return -XQC_EWRITE_PKT; } int xqc_write_ack_ext_to_one_packet(xqc_connection_t *conn, xqc_packet_out_t *packet_out, xqc_pkt_num_space_t pns, xqc_bool_t is_new_pkt) { ssize_t ret; int has_gap; xqc_packet_number_t largest_ack; xqc_usec_t now = xqc_monotonic_timestamp(); xqc_path_ctx_t *path = conn->conn_initial_path; xqc_pn_ctl_t *pn_ctl = xqc_get_pn_ctl(conn, path); if (path->recv_ts_info->nobuf_for_ts_in_last_ext_ack && !is_new_pkt) { return -XQC_ENOBUF; } ret = xqc_gen_ack_ext_frame(conn, packet_out, now, conn->local_settings.ack_delay_exponent, &pn_ctl->ctl_recv_record[pns], path->path_send_ctl->ctl_largest_recv_time[pns], &has_gap, &largest_ack, path->recv_ts_info); if (ret < 0) { goto error; } xqc_log(conn->log, XQC_LOG_DEBUG, "|ack_size:%ui|path:%ui|path_largest_recv:%ui|frame_largest_recv:%ui|", ret, path->path_id, path->path_send_ctl->ctl_largest_received[pns], xqc_recv_record_largest(&pn_ctl->ctl_recv_record[pns])); packet_out->po_ack_offset = packet_out->po_used_size; packet_out->po_used_size += ret; packet_out->po_largest_ack = largest_ack; packet_out->po_path_flag |= XQC_PATH_SPECIFIED_BY_ACK; packet_out->po_path_id = path->path_id; path->path_send_ctl->ctl_ack_eliciting_pkt[pns] = 0; if (has_gap) { conn->conn_flag |= XQC_CONN_FLAG_ACK_HAS_GAP; } else { conn->conn_flag &= ~XQC_CONN_FLAG_ACK_HAS_GAP; } path->path_flag &= ~(XQC_PATH_FLAG_SHOULD_ACK_INIT << pns); conn->ack_flag &= ~(1 << (pns + path->path_id * XQC_PNS_N)); path->path_send_ctl->ctl_ack_sent_cnt++; return XQC_OK; error: xqc_maybe_recycle_packet_out(packet_out, conn); return ret; }