Compare commits
8 Commits
master
...
20250214-q
Author | SHA1 | Date | |
---|---|---|---|
|
2aa57e8534 | ||
|
9d03a07018 | ||
|
9990c9353b | ||
|
c87799652a | ||
|
4107fdb7be | ||
|
b741247396 | ||
|
36946bb9c3 | ||
|
3fc677c25e |
@ -184,6 +184,8 @@ struct qcs {
|
|||||||
struct tot_time buf; /* stream to QCS send blocked on buffer */
|
struct tot_time buf; /* stream to QCS send blocked on buffer */
|
||||||
struct tot_time fctl; /* stream to QCS send blocked on flow-control */
|
struct tot_time fctl; /* stream to QCS send blocked on flow-control */
|
||||||
} timer;
|
} timer;
|
||||||
|
|
||||||
|
struct buffer qos_buf;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Used as qcc_app_ops.close callback argument. */
|
/* Used as qcc_app_ops.close callback argument. */
|
||||||
@ -237,6 +239,8 @@ struct qcc_app_ops {
|
|||||||
/* unused 0x00000010 */
|
/* unused 0x00000010 */
|
||||||
#define QC_CF_ERR_CONN 0x00000020 /* fatal error reported by transport layer */
|
#define QC_CF_ERR_CONN 0x00000020 /* fatal error reported by transport layer */
|
||||||
#define QC_CF_WAIT_HS 0x00000040 /* MUX init before QUIC handshake completed (0-RTT) */
|
#define QC_CF_WAIT_HS 0x00000040 /* MUX init before QUIC handshake completed (0-RTT) */
|
||||||
|
#define QC_CF_QOS 0x00000080
|
||||||
|
#define QC_CF_QSTP_SENT 0x00000100
|
||||||
|
|
||||||
/* This function is used to report flags in debugging tools. Please reflect
|
/* This function is used to report flags in debugging tools. Please reflect
|
||||||
* below any single-bit flag addition above in the same order via the
|
* below any single-bit flag addition above in the same order via the
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
_qcc_report_glitch(qcc, inc); \
|
_qcc_report_glitch(qcc, inc); \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
int qmux_is_quic(const struct qcc *qcc);
|
||||||
|
|
||||||
void qcc_set_error(struct qcc *qcc, int err, int app);
|
void qcc_set_error(struct qcc *qcc, int err, int app);
|
||||||
int _qcc_report_glitch(struct qcc *qcc, int inc);
|
int _qcc_report_glitch(struct qcc *qcc, int inc);
|
||||||
struct qcs *qcc_init_stream_local(struct qcc *qcc, int bidi);
|
struct qcs *qcc_init_stream_local(struct qcc *qcc, int bidi);
|
||||||
|
@ -87,6 +87,7 @@ enum quic_frame_type {
|
|||||||
* defined in quic_frame.c. Do not forget to complete the associated function
|
* defined in quic_frame.c. Do not forget to complete the associated function
|
||||||
* quic_frame_type_is_known() and both qf_builder()/qf_parser().
|
* quic_frame_type_is_known() and both qf_builder()/qf_parser().
|
||||||
*/
|
*/
|
||||||
|
extern const uint64_t QUIC_FT_QS_TP;
|
||||||
|
|
||||||
#define QUIC_FT_PKT_TYPE_I_BITMASK (1 << QUIC_PACKET_TYPE_INITIAL)
|
#define QUIC_FT_PKT_TYPE_I_BITMASK (1 << QUIC_PACKET_TYPE_INITIAL)
|
||||||
#define QUIC_FT_PKT_TYPE_0_BITMASK (1 << QUIC_PACKET_TYPE_0RTT)
|
#define QUIC_FT_PKT_TYPE_0_BITMASK (1 << QUIC_PACKET_TYPE_0RTT)
|
||||||
@ -249,6 +250,10 @@ struct qf_connection_close_app {
|
|||||||
unsigned char reason_phrase[QUIC_CC_REASON_PHRASE_MAXLEN];
|
unsigned char reason_phrase[QUIC_CC_REASON_PHRASE_MAXLEN];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct qf_qs_tp {
|
||||||
|
struct quic_transport_params tps;
|
||||||
|
};
|
||||||
|
|
||||||
struct quic_frame {
|
struct quic_frame {
|
||||||
struct list list; /* List elem from parent elem (typically a Tx packet instance, a PKTNS or a MUX element). */
|
struct list list; /* List elem from parent elem (typically a Tx packet instance, a PKTNS or a MUX element). */
|
||||||
struct quic_tx_packet *pkt; /* Last Tx packet used to send the frame. */
|
struct quic_tx_packet *pkt; /* Last Tx packet used to send the frame. */
|
||||||
@ -276,6 +281,7 @@ struct quic_frame {
|
|||||||
struct qf_path_challenge_response path_challenge_response;
|
struct qf_path_challenge_response path_challenge_response;
|
||||||
struct qf_connection_close connection_close;
|
struct qf_connection_close connection_close;
|
||||||
struct qf_connection_close_app connection_close_app;
|
struct qf_connection_close_app connection_close_app;
|
||||||
|
struct qf_qs_tp qs_tp;
|
||||||
};
|
};
|
||||||
struct quic_frame *origin; /* Parent frame. Set if frame is a duplicate (used for retransmission). */
|
struct quic_frame *origin; /* Parent frame. Set if frame is a duplicate (used for retransmission). */
|
||||||
struct list reflist; /* List head containing duplicated children frames. */
|
struct list reflist; /* List head containing duplicated children frames. */
|
||||||
|
@ -120,5 +120,12 @@ static inline void quic_transport_params_dump(struct buffer *b,
|
|||||||
quic_tp_version_info_dump(b, &p->version_information, local);
|
quic_tp_version_info_dump(b, &p->version_information, local);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int quic_transport_param_enc_int(unsigned char **buf,
|
||||||
|
const unsigned char *end,
|
||||||
|
uint64_t type, uint64_t val);
|
||||||
|
int quic_transport_params_decode(struct quic_transport_params *p, int server,
|
||||||
|
const unsigned char *buf,
|
||||||
|
const unsigned char *end);
|
||||||
|
|
||||||
#endif /* USE_QUIC */
|
#endif /* USE_QUIC */
|
||||||
#endif /* _HAPROXY_QUIC_TP_H */
|
#endif /* _HAPROXY_QUIC_TP_H */
|
||||||
|
738
src/mux_quic.c
738
src/mux_quic.c
@ -38,11 +38,18 @@ DECLARE_STATIC_POOL(pool_head_qc_stream_rxbuf, "qc_stream_rxbuf", sizeof(struct
|
|||||||
|
|
||||||
static void qmux_ctrl_send(struct qc_stream_desc *, uint64_t data, uint64_t offset);
|
static void qmux_ctrl_send(struct qc_stream_desc *, uint64_t data, uint64_t offset);
|
||||||
static void qmux_ctrl_room(struct qc_stream_desc *, uint64_t room);
|
static void qmux_ctrl_room(struct qc_stream_desc *, uint64_t room);
|
||||||
|
static void qmux_qos_ctrl_send(struct qcs *qcs, uint64_t data, uint64_t offset);
|
||||||
|
|
||||||
|
int qmux_is_quic(const struct qcc *qcc)
|
||||||
|
{
|
||||||
|
return !(qcc->flags & QC_CF_QOS);
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns true if pacing should be used for <conn> connection. */
|
/* Returns true if pacing should be used for <conn> connection. */
|
||||||
static int qcc_is_pacing_active(const struct connection *conn)
|
static int qcc_is_pacing_active(const struct connection *conn)
|
||||||
{
|
{
|
||||||
return !(quic_tune.options & QUIC_TUNE_NO_PACING);
|
struct qcc *qcc = conn->ctx;
|
||||||
|
return qmux_is_quic(qcc) && !(quic_tune.options & QUIC_TUNE_NO_PACING);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free <rxbuf> instance and its inner data storage attached to <qcs> stream. */
|
/* Free <rxbuf> instance and its inner data storage attached to <qcs> stream. */
|
||||||
@ -181,6 +188,10 @@ static struct qcs *qcs_new(struct qcc *qcc, uint64_t id, enum qcs_type type)
|
|||||||
|
|
||||||
qcs->err = 0;
|
qcs->err = 0;
|
||||||
|
|
||||||
|
qcs->qos_buf = BUF_NULL;
|
||||||
|
b_alloc(&qcs->qos_buf, DB_MUX_TX);
|
||||||
|
BUG_ON(!b_size(&qcs->qos_buf));
|
||||||
|
|
||||||
/* Reset all timers and start base one. */
|
/* Reset all timers and start base one. */
|
||||||
tot_time_reset(&qcs->timer.base);
|
tot_time_reset(&qcs->timer.base);
|
||||||
tot_time_reset(&qcs->timer.buf);
|
tot_time_reset(&qcs->timer.buf);
|
||||||
@ -198,17 +209,19 @@ static struct qcs *qcs_new(struct qcc *qcc, uint64_t id, enum qcs_type type)
|
|||||||
if (!(global.tune.no_zero_copy_fwd & NO_ZERO_COPY_FWD_QUIC_SND))
|
if (!(global.tune.no_zero_copy_fwd & NO_ZERO_COPY_FWD_QUIC_SND))
|
||||||
se_fl_set(qcs->sd, SE_FL_MAY_FASTFWD_CONS);
|
se_fl_set(qcs->sd, SE_FL_MAY_FASTFWD_CONS);
|
||||||
|
|
||||||
/* Allocate transport layer stream descriptor. Only needed for TX. */
|
if (qmux_is_quic(qcc)) {
|
||||||
if (!quic_stream_is_uni(id) || !quic_stream_is_remote(qcc, id)) {
|
/* Allocate transport layer stream descriptor. Only needed for TX. */
|
||||||
struct quic_conn *qc = qcc->conn->handle.qc;
|
if (!quic_stream_is_uni(id) || !quic_stream_is_remote(qcc, id)) {
|
||||||
qcs->stream = qc_stream_desc_new(id, type, qcs, qc);
|
struct quic_conn *qc = qcc->conn->handle.qc;
|
||||||
if (!qcs->stream) {
|
qcs->stream = qc_stream_desc_new(id, type, qcs, qc);
|
||||||
TRACE_ERROR("qc_stream_desc alloc failure", QMUX_EV_QCS_NEW, qcc->conn, qcs);
|
if (!qcs->stream) {
|
||||||
goto err;
|
TRACE_ERROR("qc_stream_desc alloc failure", QMUX_EV_QCS_NEW, qcc->conn, qcs);
|
||||||
}
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
qc_stream_desc_sub_send(qcs->stream, qmux_ctrl_send);
|
qc_stream_desc_sub_send(qcs->stream, qmux_ctrl_send);
|
||||||
qc_stream_desc_sub_room(qcs->stream, qmux_ctrl_room);
|
qc_stream_desc_sub_room(qcs->stream, qmux_ctrl_room);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qcc->app_ops->attach && qcc->app_ops->attach(qcs, qcc->ctx)) {
|
if (qcc->app_ops->attach && qcc->app_ops->attach(qcs, qcc->ctx)) {
|
||||||
@ -571,19 +584,36 @@ void qcs_notify_send(struct qcs *qcs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const struct buffer *qcs_tx_buf_const(const struct qcs *qcs)
|
||||||
|
{
|
||||||
|
return qmux_is_quic(qcs->qcc) ?
|
||||||
|
qc_stream_buf_get(qcs->stream) : &qcs->qos_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct buffer *qcs_tx_buf(struct qcs *qcs)
|
||||||
|
{
|
||||||
|
return qmux_is_quic(qcs->qcc) ?
|
||||||
|
qc_stream_buf_get(qcs->stream) : &qcs->qos_buf;
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns total number of bytes not already sent to quic-conn layer. */
|
/* Returns total number of bytes not already sent to quic-conn layer. */
|
||||||
static uint64_t qcs_prep_bytes(const struct qcs *qcs)
|
static uint64_t qcs_prep_bytes(const struct qcs *qcs)
|
||||||
{
|
{
|
||||||
struct buffer *out = qc_stream_buf_get(qcs->stream);
|
const struct buffer *out = qcs_tx_buf_const(qcs);
|
||||||
uint64_t diff, base_off;
|
uint64_t diff, base_off;
|
||||||
|
|
||||||
if (!out)
|
if (!out)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* if ack_offset < buf_offset, it points to an older buffer. */
|
if (qmux_is_quic(qcs->qcc)) {
|
||||||
base_off = MAX(qcs->stream->buf_offset, qcs->stream->ack_offset);
|
/* if ack_offset < buf_offset, it points to an older buffer. */
|
||||||
diff = qcs->tx.fc.off_real - base_off;
|
base_off = MAX(qcs->stream->buf_offset, qcs->stream->ack_offset);
|
||||||
return b_data(out) - diff;
|
diff = qcs->tx.fc.off_real - base_off;
|
||||||
|
return b_data(out) - diff;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return b_data(out);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Used as a callback for qc_stream_desc layer to notify about emission of a
|
/* Used as a callback for qc_stream_desc layer to notify about emission of a
|
||||||
@ -681,8 +711,13 @@ static void qmux_ctrl_send(struct qc_stream_desc *stream, uint64_t data, uint64_
|
|||||||
/* Returns true if <qcc> buffer window does not have room for a new buffer. */
|
/* Returns true if <qcc> buffer window does not have room for a new buffer. */
|
||||||
static inline int qcc_bufwnd_full(const struct qcc *qcc)
|
static inline int qcc_bufwnd_full(const struct qcc *qcc)
|
||||||
{
|
{
|
||||||
const struct quic_conn *qc = qcc->conn->handle.qc;
|
if (qmux_is_quic(qcc)) {
|
||||||
return qcc->tx.buf_in_flight >= qc->path->cwnd;
|
const struct quic_conn *qc = qcc->conn->handle.qc;
|
||||||
|
return qcc->tx.buf_in_flight >= qc->path->cwnd;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qmux_ctrl_room(struct qc_stream_desc *stream, uint64_t room)
|
static void qmux_ctrl_room(struct qc_stream_desc *stream, uint64_t room)
|
||||||
@ -895,13 +930,15 @@ static struct qcs *qcc_init_stream_remote(struct qcc *qcc, uint64_t id)
|
|||||||
*/
|
*/
|
||||||
void qcs_send_metadata(struct qcs *qcs)
|
void qcs_send_metadata(struct qcs *qcs)
|
||||||
{
|
{
|
||||||
/* Reserved for stream with Tx capability. */
|
if (qmux_is_quic(qcs->qcc)) {
|
||||||
BUG_ON(!qcs->stream);
|
/* Reserved for stream with Tx capability. */
|
||||||
/* Cannot use if some data already transferred for this stream. */
|
BUG_ON(!qcs->stream);
|
||||||
BUG_ON(qcs->stream->ack_offset || !eb_is_empty(&qcs->stream->buf_tree));
|
/* Cannot use if some data already transferred for this stream. */
|
||||||
|
BUG_ON(qcs->stream->ack_offset || !eb_is_empty(&qcs->stream->buf_tree));
|
||||||
|
|
||||||
qcs->flags |= QC_SF_TXBUB_OOB;
|
qcs->flags |= QC_SF_TXBUB_OOB;
|
||||||
qc_stream_desc_sub_room(qcs->stream, NULL);
|
qc_stream_desc_sub_room(qcs->stream, NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Instantiate a streamdesc instance for <qcs> stream. This is necessary to
|
/* Instantiate a streamdesc instance for <qcs> stream. This is necessary to
|
||||||
@ -1373,7 +1410,7 @@ struct buffer *qcc_get_stream_rxbuf(struct qcs *qcs)
|
|||||||
struct buffer *qcc_get_stream_txbuf(struct qcs *qcs, int *err, int small)
|
struct buffer *qcc_get_stream_txbuf(struct qcs *qcs, int *err, int small)
|
||||||
{
|
{
|
||||||
struct qcc *qcc = qcs->qcc;
|
struct qcc *qcc = qcs->qcc;
|
||||||
struct buffer *out = qc_stream_buf_get(qcs->stream);
|
struct buffer *out = qcs_tx_buf(qcs);
|
||||||
|
|
||||||
/* Stream must not try to reallocate a buffer if currently waiting for one. */
|
/* Stream must not try to reallocate a buffer if currently waiting for one. */
|
||||||
BUG_ON(LIST_INLIST(&qcs->el_buf));
|
BUG_ON(LIST_INLIST(&qcs->el_buf));
|
||||||
@ -1529,8 +1566,10 @@ static void _qcc_send_stream(struct qcs *qcs, int urg)
|
|||||||
qcc_clear_frms(qcc);
|
qcc_clear_frms(qcc);
|
||||||
|
|
||||||
if (urg) {
|
if (urg) {
|
||||||
/* qcc_emit_rs_ss() relies on reset/aborted streams in send_list front. */
|
if (qmux_is_quic(qcc)) {
|
||||||
BUG_ON(!(qcs->flags & (QC_SF_TO_RESET|QC_SF_TO_STOP_SENDING|QC_SF_TXBUB_OOB)));
|
/* qcc_emit_rs_ss() relies on reset/aborted streams in send_list front. */
|
||||||
|
BUG_ON(!(qcs->flags & (QC_SF_TO_RESET|QC_SF_TO_STOP_SENDING|QC_SF_TXBUB_OOB)));
|
||||||
|
}
|
||||||
|
|
||||||
LIST_DEL_INIT(&qcs->el_send);
|
LIST_DEL_INIT(&qcs->el_send);
|
||||||
LIST_INSERT(&qcc->send_list, &qcs->el_send);
|
LIST_INSERT(&qcc->send_list, &qcs->el_send);
|
||||||
@ -2312,7 +2351,7 @@ static int qcs_build_stream_frm(struct qcs *qcs, struct buffer *out, char fin,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
frm->stream.stream = qcs->stream;
|
frm->stream.stream = qmux_is_quic(qcc) ? qcs->stream : (struct qc_stream_desc *)qcs;
|
||||||
frm->stream.id = qcs->id;
|
frm->stream.id = qcs->id;
|
||||||
frm->stream.offset = 0;
|
frm->stream.offset = 0;
|
||||||
frm->stream.dup = 0;
|
frm->stream.dup = 0;
|
||||||
@ -2378,13 +2417,7 @@ static int qcc_subscribe_send(struct qcc *qcc)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wrapper for send on transport layer. Send a list of frames <frms> for the
|
static int _qcc_send_frames(struct qcc *qcc, struct list *frms, int stream)
|
||||||
* connection <qcc>.
|
|
||||||
*
|
|
||||||
* Returns 0 if all data sent with success. On fatal error, a negative error
|
|
||||||
* code is returned. A positive 1 is used if emission should be paced.
|
|
||||||
*/
|
|
||||||
static int qcc_send_frames(struct qcc *qcc, struct list *frms, int stream)
|
|
||||||
{
|
{
|
||||||
enum quic_tx_err ret;
|
enum quic_tx_err ret;
|
||||||
struct quic_pacer *pacer = NULL;
|
struct quic_pacer *pacer = NULL;
|
||||||
@ -2423,6 +2456,105 @@ static int qcc_send_frames(struct qcc *qcc, struct list *frms, int stream)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Wrapper for send on transport layer. Send a list of frames <frms> for the
|
||||||
|
* connection <qcc>.
|
||||||
|
*
|
||||||
|
* Returns 0 if all data sent with success. On fatal error, a negative error
|
||||||
|
* code is returned. A positive 1 is used if emission should be paced.
|
||||||
|
*/
|
||||||
|
static int _qcc_qos_send_frames(struct qcc *qcc, struct list *frms, int stream)
|
||||||
|
{
|
||||||
|
struct connection *conn = qcc->conn;
|
||||||
|
struct quic_frame *frm, *frm_old;
|
||||||
|
unsigned char *pos, *old, *end;
|
||||||
|
size_t ret;
|
||||||
|
|
||||||
|
TRACE_ENTER(QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
list_for_each_entry_safe(frm, frm_old, frms, list) {
|
||||||
|
loop:
|
||||||
|
struct quic_frame *split_frm = NULL, *old_frm;
|
||||||
|
|
||||||
|
b_reset(&trash);
|
||||||
|
old = pos = (unsigned char *)b_orig(&trash);
|
||||||
|
end = (unsigned char *)b_wrap(&trash);
|
||||||
|
|
||||||
|
TRACE_PRINTF(TRACE_LEVEL_DEVELOPER, QMUX_EV_QCC_SEND, qcc->conn, 0, 0, 0,
|
||||||
|
"frm type %02llx", (ullong)frm->type);
|
||||||
|
|
||||||
|
if (frm->type >= QUIC_FT_STREAM_8 && frm->type <= QUIC_FT_STREAM_F) {
|
||||||
|
size_t flen, split_size;
|
||||||
|
|
||||||
|
flen = quic_strm_frm_fillbuf(end - pos, frm, &split_size);
|
||||||
|
if (!flen)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (split_size) {
|
||||||
|
split_frm = quic_strm_frm_split(frm, split_size);
|
||||||
|
if (!split_frm) {
|
||||||
|
ABORT_NOW();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_frm = frm;
|
||||||
|
frm = split_frm;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
qc_build_frm(&pos, end, frm, NULL, NULL);
|
||||||
|
BUG_ON(pos - old > global.tune.bufsize);
|
||||||
|
BUG_ON(pos == old);
|
||||||
|
b_add(&trash, pos - old);
|
||||||
|
|
||||||
|
ret = conn->xprt->snd_buf(conn, conn->xprt_ctx, &trash, b_data(&trash), 0);
|
||||||
|
if (!ret) {
|
||||||
|
TRACE_DEVEL("snd_buf interrupted", QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
if (split_frm)
|
||||||
|
LIST_INSERT(frms, &split_frm->list);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != b_data(&trash)) {
|
||||||
|
/* TODO */
|
||||||
|
ABORT_NOW();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frm->type >= QUIC_FT_STREAM_8 && frm->type <= QUIC_FT_STREAM_E) {
|
||||||
|
qmux_qos_ctrl_send((struct qcs *)frm->stream.stream,
|
||||||
|
frm->stream.len, frm->stream.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
LIST_DEL_INIT(&frm->list);
|
||||||
|
if (split_frm) {
|
||||||
|
frm = old_frm;
|
||||||
|
goto loop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->flags & CO_FL_ERROR) {
|
||||||
|
/* TODO */
|
||||||
|
//ABORT_NOW();
|
||||||
|
}
|
||||||
|
else if (!LIST_ISEMPTY(frms) && !(qcc->wait_event.events & SUB_RETRY_SEND)) {
|
||||||
|
conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_SEND, &qcc->wait_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE_LEAVE(QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wrapper for send on transport layer. Send a list of frames <frms> for the
|
||||||
|
* connection <qcc>.
|
||||||
|
*
|
||||||
|
* Returns 0 if all data sent with success. On fatal error, a negative error
|
||||||
|
* code is returned. A positive 1 is used if emission should be paced.
|
||||||
|
*/
|
||||||
|
static int qcc_send_frames(struct qcc *qcc, struct list *frms, int stream)
|
||||||
|
{
|
||||||
|
return qmux_is_quic(qcc) ? _qcc_send_frames(qcc, frms, stream) :
|
||||||
|
_qcc_qos_send_frames(qcc, frms, stream);
|
||||||
|
}
|
||||||
|
|
||||||
/* Emit a RESET_STREAM on <qcs>.
|
/* Emit a RESET_STREAM on <qcs>.
|
||||||
*
|
*
|
||||||
* Returns 0 if the frame has been successfully sent else non-zero.
|
* Returns 0 if the frame has been successfully sent else non-zero.
|
||||||
@ -2521,7 +2653,7 @@ static int qcs_send_stop_sending(struct qcs *qcs)
|
|||||||
static int qcs_send(struct qcs *qcs, struct list *frms, uint64_t window_conn)
|
static int qcs_send(struct qcs *qcs, struct list *frms, uint64_t window_conn)
|
||||||
{
|
{
|
||||||
struct qcc *qcc = qcs->qcc;
|
struct qcc *qcc = qcs->qcc;
|
||||||
struct buffer *out = qc_stream_buf_get(qcs->stream);
|
struct buffer *out = qcs_tx_buf(qcs);
|
||||||
int flen = 0;
|
int flen = 0;
|
||||||
const char fin = qcs->flags & QC_SF_FIN_STREAM;
|
const char fin = qcs->flags & QC_SF_FIN_STREAM;
|
||||||
|
|
||||||
@ -2571,7 +2703,7 @@ static int qcc_emit_rs_ss(struct qcc *qcc)
|
|||||||
list_for_each_entry_safe(qcs, qcs_tmp, &qcc->send_list, el_send) {
|
list_for_each_entry_safe(qcs, qcs_tmp, &qcc->send_list, el_send) {
|
||||||
/* Stream must not be present in send_list if it has nothing to send. */
|
/* Stream must not be present in send_list if it has nothing to send. */
|
||||||
BUG_ON(!(qcs->flags & (QC_SF_FIN_STREAM|QC_SF_TO_STOP_SENDING|QC_SF_TO_RESET)) &&
|
BUG_ON(!(qcs->flags & (QC_SF_FIN_STREAM|QC_SF_TO_STOP_SENDING|QC_SF_TO_RESET)) &&
|
||||||
(!qcs->stream || !qcs_prep_bytes(qcs)));
|
(qmux_is_quic(qcc) && (!qcs->stream || !qcs_prep_bytes(qcs))));
|
||||||
|
|
||||||
/* Interrupt looping for the first stream where no RS nor SS is
|
/* Interrupt looping for the first stream where no RS nor SS is
|
||||||
* necessary and is not use for "metadata" transfer. These
|
* necessary and is not use for "metadata" transfer. These
|
||||||
@ -2666,7 +2798,7 @@ static int qcc_build_frms(struct qcc *qcc, struct list *qcs_failed)
|
|||||||
/* Streams with RS/SS must be handled via qcc_emit_rs_ss(). */
|
/* Streams with RS/SS must be handled via qcc_emit_rs_ss(). */
|
||||||
BUG_ON(qcs->flags & (QC_SF_TO_STOP_SENDING|QC_SF_TO_RESET));
|
BUG_ON(qcs->flags & (QC_SF_TO_STOP_SENDING|QC_SF_TO_RESET));
|
||||||
/* Stream must not be present in send_list if it has nothing to send. */
|
/* Stream must not be present in send_list if it has nothing to send. */
|
||||||
BUG_ON(!(qcs->flags & QC_SF_FIN_STREAM) && (!qcs->stream || !qcs_prep_bytes(qcs)));
|
BUG_ON(!(qcs->flags & QC_SF_FIN_STREAM) && (qmux_is_quic(qcc) && (!qcs->stream || !qcs_prep_bytes(qcs))));
|
||||||
|
|
||||||
/* Total sent bytes must not exceed connection window. */
|
/* Total sent bytes must not exceed connection window. */
|
||||||
BUG_ON(total > window_conn);
|
BUG_ON(total > window_conn);
|
||||||
@ -2917,6 +3049,11 @@ static void qcc_wait_for_hs(struct qcc *qcc)
|
|||||||
|
|
||||||
qcc->flags &= ~QC_CF_WAIT_HS;
|
qcc->flags &= ~QC_CF_WAIT_HS;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
/* subscribe for handshake completion */
|
||||||
|
conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV,
|
||||||
|
&qcc->wait_event);
|
||||||
|
}
|
||||||
|
|
||||||
TRACE_LEAVE(QMUX_EV_QCC_RECV, qcc->conn);
|
TRACE_LEAVE(QMUX_EV_QCC_RECV, qcc->conn);
|
||||||
}
|
}
|
||||||
@ -2995,24 +3132,28 @@ static void qcc_shutdown(struct qcc *qcc)
|
|||||||
if (qcc->app_st >= QCC_APP_ST_SHUT)
|
if (qcc->app_st >= QCC_APP_ST_SHUT)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
TRACE_STATE("perform graceful shutdown", QMUX_EV_QCC_END, qcc->conn);
|
if (qmux_is_quic(qcc)) {
|
||||||
if (qcc->app_ops && qcc->app_ops->shutdown) {
|
TRACE_STATE("perform graceful shutdown", QMUX_EV_QCC_END, qcc->conn);
|
||||||
qcc->app_ops->shutdown(qcc->ctx);
|
if (qcc->app_ops && qcc->app_ops->shutdown) {
|
||||||
qcc_io_send(qcc);
|
qcc->app_ops->shutdown(qcc->ctx);
|
||||||
}
|
qcc_io_send(qcc);
|
||||||
else {
|
}
|
||||||
qcc->err = quic_err_transport(QC_ERR_NO_ERROR);
|
else {
|
||||||
|
qcc->err = quic_err_transport(QC_ERR_NO_ERROR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Register "no error" code at transport layer. Do not use
|
if (qmux_is_quic(qcc)) {
|
||||||
* quic_set_connection_close() as retransmission may be performed to
|
/* Register "no error" code at transport layer. Do not use
|
||||||
* finalized transfers. Do not overwrite quic-conn existing code if
|
* quic_set_connection_close() as retransmission may be performed to
|
||||||
* already set.
|
* finalized transfers. Do not overwrite quic-conn existing code if
|
||||||
*
|
* already set.
|
||||||
* TODO implement a wrapper function for this in quic-conn module
|
*
|
||||||
*/
|
* TODO implement a wrapper function for this in quic-conn module
|
||||||
if (!(qcc->conn->handle.qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE))
|
*/
|
||||||
qcc->conn->handle.qc->err = qcc->err;
|
if (!(qcc->conn->handle.qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE))
|
||||||
|
qcc->conn->handle.qc->err = qcc->err;
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
qcc->app_st = QCC_APP_ST_SHUT;
|
qcc->app_st = QCC_APP_ST_SHUT;
|
||||||
@ -3132,7 +3273,7 @@ static void qcc_release(struct qcc *qcc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* unsubscribe from all remaining qc_stream_desc */
|
/* unsubscribe from all remaining qc_stream_desc */
|
||||||
if (conn) {
|
if (qmux_is_quic(qcc) && conn) {
|
||||||
qc = conn->handle.qc;
|
qc = conn->handle.qc;
|
||||||
node = eb64_first(&qc->streams_by_id);
|
node = eb64_first(&qc->streams_by_id);
|
||||||
while (node) {
|
while (node) {
|
||||||
@ -3446,9 +3587,6 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
|
|||||||
if (!(conn->flags & CO_FL_EARLY_SSL_HS)) {
|
if (!(conn->flags & CO_FL_EARLY_SSL_HS)) {
|
||||||
TRACE_STATE("flag connection with early data", QMUX_EV_QCC_WAKE, conn);
|
TRACE_STATE("flag connection with early data", QMUX_EV_QCC_WAKE, conn);
|
||||||
conn->flags |= CO_FL_EARLY_SSL_HS;
|
conn->flags |= CO_FL_EARLY_SSL_HS;
|
||||||
/* subscribe for handshake completion */
|
|
||||||
conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV,
|
|
||||||
&qcc->wait_event);
|
|
||||||
qcc->flags |= QC_CF_WAIT_HS;
|
qcc->flags |= QC_CF_WAIT_HS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4045,3 +4183,487 @@ static struct mux_proto_list mux_proto_quic =
|
|||||||
{ .token = IST("quic"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_FE, .mux = &qmux_ops };
|
{ .token = IST("quic"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_FE, .mux = &qmux_ops };
|
||||||
|
|
||||||
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_quic);
|
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_quic);
|
||||||
|
|
||||||
|
static int qcc_qos_recv(struct qcc *qcc)
|
||||||
|
{
|
||||||
|
struct connection *conn = qcc->conn;
|
||||||
|
struct quic_frame frm;
|
||||||
|
const unsigned char *pos, *end;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
TRACE_ENTER(QMUX_EV_QCC_RECV, qcc->conn);
|
||||||
|
|
||||||
|
chunk_reset(&trash);
|
||||||
|
ret = conn->xprt->rcv_buf(conn, conn->xprt_ctx, &trash, trash.size, 0);
|
||||||
|
BUG_ON(ret < 0);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
b_add(&trash, ret);
|
||||||
|
|
||||||
|
pos = (unsigned char *)b_head(&trash);
|
||||||
|
end = (unsigned char *)b_tail(&trash);
|
||||||
|
ret = qc_parse_frm(&frm, NULL, &pos, end, NULL);
|
||||||
|
BUG_ON(!ret);
|
||||||
|
|
||||||
|
if (frm.type == QUIC_FT_QS_TP) {
|
||||||
|
struct qf_qs_tp *qs_tp_frm = &frm.qs_tp;
|
||||||
|
fprintf(stderr, "got qs_transport_parameters frame\n");
|
||||||
|
fprintf(stderr, " max_idle_timeout=%llu\n", (ullong)qs_tp_frm->tps.max_idle_timeout);
|
||||||
|
fprintf(stderr, " initial_max_data=%llu\n", (ullong)qs_tp_frm->tps.initial_max_data);
|
||||||
|
qfctl_set_max(&qcc->tx.fc, qs_tp_frm->tps.initial_max_data, NULL, NULL);
|
||||||
|
fprintf(stderr, " initial_max_stream_data_bidi_local=%llu\n", (ullong)qs_tp_frm->tps.initial_max_stream_data_bidi_local);
|
||||||
|
qcc->rfctl.msd_bidi_l = qs_tp_frm->tps.initial_max_stream_data_bidi_local;
|
||||||
|
fprintf(stderr, " initial_max_stream_data_bidi_remote=%llu\n", (ullong)qs_tp_frm->tps.initial_max_stream_data_bidi_remote);
|
||||||
|
qcc->rfctl.msd_bidi_r = qs_tp_frm->tps.initial_max_stream_data_bidi_remote;
|
||||||
|
fprintf(stderr, " initial_max_stream_data_uni=%llu\n", (ullong)qs_tp_frm->tps.initial_max_stream_data_uni);
|
||||||
|
qcc->rfctl.msd_uni_l = qs_tp_frm->tps.initial_max_stream_data_uni;
|
||||||
|
fprintf(stderr, " initial_max_streams_bidi=%llu\n", (ullong)qs_tp_frm->tps.initial_max_streams_bidi);
|
||||||
|
fprintf(stderr, " initial_max_streams_uni=%llu\n", (ullong)qs_tp_frm->tps.initial_max_streams_uni);
|
||||||
|
}
|
||||||
|
else if (frm.type >= QUIC_FT_STREAM_8 &&
|
||||||
|
frm.type <= QUIC_FT_STREAM_F) {
|
||||||
|
struct qf_stream *strm_frm = &frm.stream;
|
||||||
|
|
||||||
|
qcc_recv(qcc, strm_frm->id, strm_frm->len, strm_frm->offset,
|
||||||
|
(frm.type & QUIC_STREAM_FRAME_TYPE_FIN_BIT), (char *)strm_frm->data);
|
||||||
|
}
|
||||||
|
else if (frm.type == QUIC_FT_RESET_STREAM) {
|
||||||
|
struct qf_reset_stream *rst_frm = &frm.reset_stream;
|
||||||
|
qcc_recv_reset_stream(qcc, rst_frm->id, rst_frm->app_error_code, rst_frm->final_size);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ABORT_NOW();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BUG_ON(!trash.size);
|
||||||
|
if (!conn_xprt_read0_pending(qcc->conn)) {
|
||||||
|
conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV,
|
||||||
|
&qcc->wait_event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE_LEAVE(QMUX_EV_QCC_RECV, qcc->conn);
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
err:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qcc_qos_io_recv(struct qcc *qcc)
|
||||||
|
{
|
||||||
|
struct qcs *qcs;
|
||||||
|
|
||||||
|
TRACE_ENTER(QMUX_EV_QCC_RECV, qcc->conn);
|
||||||
|
|
||||||
|
if (qcc->flags & QC_CF_ERRL) {
|
||||||
|
TRACE_DATA("connection on error", QMUX_EV_QCC_RECV, qcc->conn);
|
||||||
|
TRACE_LEAVE(QMUX_EV_QCC_RECV, qcc->conn);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((qcc->flags & QC_CF_WAIT_HS) && !(qcc->wait_event.events & SUB_RETRY_RECV))
|
||||||
|
qcc_wait_for_hs(qcc);
|
||||||
|
|
||||||
|
if (!(qcc->wait_event.events & SUB_RETRY_RECV))
|
||||||
|
qcc_qos_recv(qcc);
|
||||||
|
|
||||||
|
while (!LIST_ISEMPTY(&qcc->recv_list)) {
|
||||||
|
qcs = LIST_ELEM(qcc->recv_list.n, struct qcs *, el_recv);
|
||||||
|
/* no need to add an uni local stream in recv_list. */
|
||||||
|
BUG_ON(quic_stream_is_uni(qcs->id) && quic_stream_is_local(qcc, qcs->id));
|
||||||
|
qcc_decode_qcs(qcc, qcs);
|
||||||
|
LIST_DEL_INIT(&qcs->el_recv);
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE_LEAVE(QMUX_EV_QCC_RECV, qcc->conn);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Used as a callback for qc_stream_desc layer to notify about emission of a
|
||||||
|
* STREAM frame of <data> length starting at <offset>.
|
||||||
|
*/
|
||||||
|
static void qmux_qos_ctrl_send(struct qcs *qcs, uint64_t data, uint64_t offset)
|
||||||
|
{
|
||||||
|
struct qcc *qcc = qcs->qcc;
|
||||||
|
uint64_t diff;
|
||||||
|
|
||||||
|
TRACE_ENTER(QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
||||||
|
|
||||||
|
/* Real off MUST always be the greatest offset sent. */
|
||||||
|
BUG_ON(offset > qcs->tx.fc.off_real);
|
||||||
|
|
||||||
|
/* Check if the STREAM frame has already been notified. An empty FIN
|
||||||
|
* frame must not be considered retransmitted.
|
||||||
|
*/
|
||||||
|
if (data && offset + data <= qcs->tx.fc.off_real) {
|
||||||
|
TRACE_DEVEL("offset already notified", QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* An empty STREAM frame is only used to notify FIN. A retransmitted
|
||||||
|
* empty FIN cannot be notified as QCS will be unsubscribed first.
|
||||||
|
*/
|
||||||
|
BUG_ON(!data && !(qcs->flags & QC_SF_FIN_STREAM));
|
||||||
|
|
||||||
|
qcs_idle_open(qcs);
|
||||||
|
|
||||||
|
diff = offset + data - qcs->tx.fc.off_real;
|
||||||
|
if (diff) {
|
||||||
|
struct quic_fctl *fc_conn = &qcc->tx.fc;
|
||||||
|
struct quic_fctl *fc_strm = &qcs->tx.fc;
|
||||||
|
|
||||||
|
/* Ensure real offset never exceeds soft value. */
|
||||||
|
BUG_ON(fc_conn->off_real + diff > fc_conn->off_soft);
|
||||||
|
BUG_ON(fc_strm->off_real + diff > fc_strm->off_soft);
|
||||||
|
|
||||||
|
/* increase offset sum on connection */
|
||||||
|
if (qfctl_rinc(fc_conn, diff)) {
|
||||||
|
TRACE_STATE("connection flow-control reached",
|
||||||
|
QMUX_EV_QCS_SEND, qcc->conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* increase offset on stream */
|
||||||
|
if (qfctl_rinc(fc_strm, diff)) {
|
||||||
|
TRACE_STATE("stream flow-control reached",
|
||||||
|
QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
||||||
|
}
|
||||||
|
|
||||||
|
b_del(&qcs->qos_buf, diff);
|
||||||
|
/* Release buffer if everything sent and buf is full or stream is waiting for room. */
|
||||||
|
if (!qcs_prep_bytes(qcs) && qcs->flags & QC_SF_BLK_MROOM) {
|
||||||
|
qcs->flags &= ~QC_SF_BLK_MROOM;
|
||||||
|
qcs_notify_send(qcs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add measurement for send rate. This is done at the MUX layer
|
||||||
|
* to account only for STREAM frames without retransmission.
|
||||||
|
*/
|
||||||
|
increment_send_rate(diff, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!qcs_prep_bytes(qcs)) {
|
||||||
|
/* Remove stream from send_list if all was sent. */
|
||||||
|
LIST_DEL_INIT(&qcs->el_send);
|
||||||
|
TRACE_STATE("stream sent done", QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
||||||
|
|
||||||
|
if (qcs->flags & (QC_SF_FIN_STREAM|QC_SF_DETACH)) {
|
||||||
|
/* Close stream locally. */
|
||||||
|
qcs_close_local(qcs);
|
||||||
|
|
||||||
|
if (qcs->flags & QC_SF_FIN_STREAM) {
|
||||||
|
/* Reset flag to not emit multiple FIN STREAM frames. */
|
||||||
|
qcs->flags &= ~QC_SF_FIN_STREAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qcs_is_completed(qcs)) {
|
||||||
|
TRACE_STATE("add stream in purg_list", QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
||||||
|
LIST_APPEND(&qcc->purg_list, &qcs->el_send);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
TRACE_LEAVE(QMUX_EV_QCS_SEND, qcc->conn, qcs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qcc_qos_send_tp(struct qcc *qcc)
|
||||||
|
{
|
||||||
|
struct quic_frame *frm;
|
||||||
|
struct list list = LIST_HEAD_INIT(list);
|
||||||
|
|
||||||
|
TRACE_ENTER(QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
|
||||||
|
frm = qc_frm_alloc(QUIC_FT_QS_TP);
|
||||||
|
if (!frm) {
|
||||||
|
TRACE_ERROR("frame alloc failure", QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIST_APPEND(&list, &frm->list);
|
||||||
|
if (qcc_send_frames(qcc, &list, 0)) {
|
||||||
|
TRACE_DEVEL("QoS frames rejected by transport, aborting send", QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE_LEAVE(QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
TRACE_DEVEL("leaving on error", QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qcc_qos_io_send(struct qcc *qcc)
|
||||||
|
{
|
||||||
|
struct list *frms = &qcc->tx.frms;
|
||||||
|
/* Temporary list for QCS on error. */
|
||||||
|
struct list qcs_failed = LIST_HEAD_INIT(qcs_failed);
|
||||||
|
struct qcs *qcs, *qcs_tmp;
|
||||||
|
uint64_t window_conn __maybe_unused = qfctl_rcap(&qcc->tx.fc);
|
||||||
|
int ret __maybe_unused = 0, total = 0, resent __maybe_unused;
|
||||||
|
|
||||||
|
TRACE_ENTER(QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
|
||||||
|
if (!(qcc->flags & QC_CF_QSTP_SENT)) {
|
||||||
|
if (qcc_qos_send_tp(qcc))
|
||||||
|
return 0;
|
||||||
|
qcc->flags |= QC_CF_QSTP_SENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for transport error. */
|
||||||
|
if (qcc->flags & QC_CF_ERR_CONN || qcc->conn->flags & CO_FL_ERROR) {
|
||||||
|
TRACE_DEVEL("connection on error", QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for locally detected connection error. */
|
||||||
|
if (qcc->flags & QC_CF_ERRL) {
|
||||||
|
/* Prepare a CONNECTION_CLOSE if not already done. */
|
||||||
|
if (!(qcc->flags & QC_CF_ERRL_DONE)) {
|
||||||
|
TRACE_DATA("report a connection error", QMUX_EV_QCC_SEND|QMUX_EV_QCC_ERR, qcc->conn);
|
||||||
|
quic_set_connection_close(qcc->conn->handle.qc, qcc->err);
|
||||||
|
qcc->flags |= QC_CF_ERRL_DONE;
|
||||||
|
}
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qcc->app_st < QCC_APP_ST_INIT) {
|
||||||
|
if (qcc_app_init(qcc))
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qcc->conn->flags & CO_FL_SOCK_WR_SH) {
|
||||||
|
qcc->conn->flags |= CO_FL_ERROR;
|
||||||
|
TRACE_DEVEL("connection on error", QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!LIST_ISEMPTY(&qcc->lfctl.frms)) {
|
||||||
|
if (qcc_send_frames(qcc, &qcc->lfctl.frms, 0)) {
|
||||||
|
TRACE_DEVEL("flow-control frames rejected by transport, aborting send", QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qcc_emit_rs_ss(qcc)) {
|
||||||
|
TRACE_DEVEL("emission interrupted on STOP_SENDING/RESET_STREAM send error", QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encode new STREAM frames if list has been previously cleared. */
|
||||||
|
if (LIST_ISEMPTY(frms) && !LIST_ISEMPTY(&qcc->send_list)) {
|
||||||
|
total = qcc_build_frms(qcc, &qcs_failed);
|
||||||
|
if (LIST_ISEMPTY(frms))
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = qcc_send_frames(qcc, frms, 1);
|
||||||
|
|
||||||
|
out:
|
||||||
|
/* Re-insert on-error QCS at the end of the send-list. */
|
||||||
|
if (!LIST_ISEMPTY(&qcs_failed)) {
|
||||||
|
list_for_each_entry_safe(qcs, qcs_tmp, &qcs_failed, el_send) {
|
||||||
|
LIST_DEL_INIT(&qcs->el_send);
|
||||||
|
LIST_APPEND(&qcc->send_list, &qcs->el_send);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!qfctl_rblocked(&qcc->tx.fc))
|
||||||
|
tasklet_wakeup(qcc->wait_event.tasklet);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qcc->conn->flags & CO_FL_ERROR && !(qcc->flags & QC_CF_ERR_CONN)) {
|
||||||
|
TRACE_ERROR("error reported by transport layer",
|
||||||
|
QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
qcc->flags |= QC_CF_ERR_CONN;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE_LEAVE(QMUX_EV_QCC_SEND, qcc->conn);
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct task *qcc_qos_io_cb(struct task *t, void *ctx, unsigned int status)
|
||||||
|
{
|
||||||
|
struct qcc *qcc = ctx;
|
||||||
|
|
||||||
|
TRACE_ENTER(QMUX_EV_QCC_WAKE, qcc->conn);
|
||||||
|
|
||||||
|
qcc_qos_io_recv(qcc);
|
||||||
|
|
||||||
|
if (!(qcc->wait_event.events & SUB_RETRY_SEND))
|
||||||
|
qcc_qos_io_send(qcc);
|
||||||
|
|
||||||
|
qcc_qos_io_recv(qcc);
|
||||||
|
|
||||||
|
if (qcc_io_process(qcc)) {
|
||||||
|
TRACE_STATE("releasing dead connection", QMUX_EV_QCC_WAKE, qcc->conn);
|
||||||
|
goto release;
|
||||||
|
}
|
||||||
|
|
||||||
|
qcc_refresh_timeout(qcc);
|
||||||
|
|
||||||
|
TRACE_LEAVE(QMUX_EV_QCC_WAKE, qcc->conn);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
release:
|
||||||
|
qcc_shutdown(qcc);
|
||||||
|
qcc_release(qcc);
|
||||||
|
|
||||||
|
TRACE_LEAVE(QMUX_EV_QCC_WAKE);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qmux_qos_init(struct connection *conn, struct proxy *prx,
|
||||||
|
struct session *sess, struct buffer *input)
|
||||||
|
{
|
||||||
|
struct qcc *qcc;
|
||||||
|
|
||||||
|
TRACE_ENTER(QMUX_EV_QCC_NEW);
|
||||||
|
|
||||||
|
qcc = pool_alloc(pool_head_qcc);
|
||||||
|
if (!qcc) {
|
||||||
|
TRACE_ERROR("alloc failure", QMUX_EV_QCC_NEW);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
_qcc_init(qcc);
|
||||||
|
conn->ctx = qcc;
|
||||||
|
qcc->nb_hreq = qcc->nb_sc = 0;
|
||||||
|
qcc->flags = QC_CF_QOS;
|
||||||
|
qcc->app_st = QCC_APP_ST_NULL;
|
||||||
|
qcc->glitches = 0;
|
||||||
|
qcc->err = quic_err_transport(QC_ERR_NO_ERROR);
|
||||||
|
|
||||||
|
/* hardcoded inital TP values. Is this really necessary? */
|
||||||
|
qcc->lfctl.ms_bidi = qcc->lfctl.ms_bidi_init = 16384;
|
||||||
|
qcc->lfctl.ms_uni = 3;
|
||||||
|
qcc->lfctl.msd_bidi_l = 16384;
|
||||||
|
qcc->lfctl.msd_bidi_r = 16384;
|
||||||
|
qcc->lfctl.msd_uni_r = 16384;
|
||||||
|
qcc->lfctl.cl_bidi_r = 0;
|
||||||
|
|
||||||
|
qcc->lfctl.md = qcc->lfctl.md_init = 16384;
|
||||||
|
qcc->lfctl.offsets_recv = qcc->lfctl.offsets_consume = 0;
|
||||||
|
|
||||||
|
qfctl_init(&qcc->tx.fc, 0);
|
||||||
|
|
||||||
|
qcc->tx.buf_in_flight = 0;
|
||||||
|
|
||||||
|
if (conn_is_back(conn)) {
|
||||||
|
qcc->next_bidi_l = 0x00;
|
||||||
|
qcc->largest_bidi_r = 0x01;
|
||||||
|
qcc->next_uni_l = 0x02;
|
||||||
|
qcc->largest_uni_r = 0x03;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qcc->largest_bidi_r = 0x00;
|
||||||
|
qcc->next_bidi_l = 0x01;
|
||||||
|
qcc->largest_uni_r = 0x02;
|
||||||
|
qcc->next_uni_l = 0x03;
|
||||||
|
}
|
||||||
|
|
||||||
|
qcc->wait_event.tasklet = tasklet_new();
|
||||||
|
if (!qcc->wait_event.tasklet) {
|
||||||
|
TRACE_ERROR("taslket alloc failure", QMUX_EV_QCC_NEW);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIST_INIT(&qcc->recv_list);
|
||||||
|
LIST_INIT(&qcc->send_list);
|
||||||
|
LIST_INIT(&qcc->fctl_list);
|
||||||
|
LIST_INIT(&qcc->buf_wait_list);
|
||||||
|
LIST_INIT(&qcc->purg_list);
|
||||||
|
|
||||||
|
qcc->wait_event.tasklet->process = qcc_qos_io_cb;
|
||||||
|
qcc->wait_event.tasklet->context = qcc;
|
||||||
|
qcc->wait_event.tasklet->state |= TASK_F_WANTS_TIME;
|
||||||
|
qcc->wait_event.events = 0;
|
||||||
|
|
||||||
|
qcc->proxy = prx;
|
||||||
|
/* haproxy timeouts */
|
||||||
|
if (conn_is_back(conn)) {
|
||||||
|
qcc->timeout = prx->timeout.server;
|
||||||
|
qcc->shut_timeout = tick_isset(prx->timeout.serverfin) ?
|
||||||
|
prx->timeout.serverfin : prx->timeout.server;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qcc->timeout = prx->timeout.client;
|
||||||
|
qcc->shut_timeout = tick_isset(prx->timeout.clientfin) ?
|
||||||
|
prx->timeout.clientfin : prx->timeout.client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Always allocate task even if timeout is unset. In MUX code, if task
|
||||||
|
* is NULL, it indicates that a timeout has stroke earlier.
|
||||||
|
*/
|
||||||
|
qcc->task = task_new_here();
|
||||||
|
if (!qcc->task) {
|
||||||
|
TRACE_ERROR("timeout task alloc failure", QMUX_EV_QCC_NEW);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
qcc->task->process = qcc_timeout_task;
|
||||||
|
qcc->task->context = qcc;
|
||||||
|
qcc->task->expire = tick_add_ifset(now_ms, qcc->timeout);
|
||||||
|
|
||||||
|
qcc_reset_idle_start(qcc);
|
||||||
|
LIST_INIT(&qcc->opening_list);
|
||||||
|
|
||||||
|
/* Register conn as app_ops may use it. */
|
||||||
|
qcc->conn = conn;
|
||||||
|
|
||||||
|
/* TODO hardcoded HTTP/3 ops */
|
||||||
|
if (qcc_install_app_ops(qcc, &h3_ops)) {
|
||||||
|
TRACE_PROTO("Cannot install app layer", QMUX_EV_QCC_NEW|QMUX_EV_QCC_ERR, conn);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qcc->app_ops == &h3_ops)
|
||||||
|
proxy_inc_fe_cum_sess_ver_ctr(sess->listener, prx, 3);
|
||||||
|
|
||||||
|
/* Register conn for idle front closing. This is done once everything is allocated. */
|
||||||
|
if (!conn_is_back(conn))
|
||||||
|
LIST_APPEND(&mux_stopping_data[tid].list, &conn->stopping_list);
|
||||||
|
|
||||||
|
/* init read cycle */
|
||||||
|
tasklet_wakeup(qcc->wait_event.tasklet);
|
||||||
|
|
||||||
|
TRACE_LEAVE(QMUX_EV_QCC_NEW, conn);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (qcc) {
|
||||||
|
/* In case of MUX init failure, session will ensure connection is freed. */
|
||||||
|
qcc->conn = NULL;
|
||||||
|
qcc_release(qcc);
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE_DEVEL("leaving on error", QMUX_EV_QCC_NEW, conn);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct mux_ops qmux_qos_ops = {
|
||||||
|
.init = qmux_qos_init,
|
||||||
|
.destroy = qmux_destroy,
|
||||||
|
.detach = qmux_strm_detach,
|
||||||
|
.rcv_buf = qmux_strm_rcv_buf,
|
||||||
|
.snd_buf = qmux_strm_snd_buf,
|
||||||
|
.nego_fastfwd = qmux_strm_nego_ff,
|
||||||
|
.done_fastfwd = qmux_strm_done_ff,
|
||||||
|
.resume_fastfwd = qmux_strm_resume_ff,
|
||||||
|
.subscribe = qmux_strm_subscribe,
|
||||||
|
.unsubscribe = qmux_strm_unsubscribe,
|
||||||
|
.wake = qmux_wake,
|
||||||
|
.shut = qmux_strm_shut,
|
||||||
|
.ctl = qmux_ctl,
|
||||||
|
.sctl = qmux_sctl,
|
||||||
|
.show_sd = qmux_strm_show_sd,
|
||||||
|
.flags = MX_FL_HTX|MX_FL_NO_UPG,
|
||||||
|
.name = "QOS",
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct mux_proto_list mux_proto_qos =
|
||||||
|
{ .token = IST("qos"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_FE, .mux = &qmux_qos_ops };
|
||||||
|
|
||||||
|
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_qos);
|
||||||
|
@ -142,18 +142,19 @@ static char *qcc_app_st_to_str(const enum qcc_app_st st)
|
|||||||
|
|
||||||
void qmux_dump_qcc_info(struct buffer *msg, const struct qcc *qcc)
|
void qmux_dump_qcc_info(struct buffer *msg, const struct qcc *qcc)
|
||||||
{
|
{
|
||||||
const struct quic_conn *qc = qcc->conn->handle.qc;
|
const struct quic_conn *qc = qmux_is_quic(qcc) ? qcc->conn->handle.qc : NULL;
|
||||||
|
|
||||||
chunk_appendf(msg, " qcc=%p(F)", qcc);
|
chunk_appendf(msg, " qcc=%p(F)", qcc);
|
||||||
if (qcc->conn->handle.qc)
|
if (qc)
|
||||||
chunk_appendf(msg, " qc=%p", qcc->conn->handle.qc);
|
chunk_appendf(msg, " qc=%p", qcc->conn->handle.qc);
|
||||||
chunk_appendf(msg, " .st=%s .sc=%llu .hreq=%llu .flg=0x%04x",
|
chunk_appendf(msg, " .st=%s .sc=%llu .hreq=%llu .flg=0x%04x",
|
||||||
qcc_app_st_to_str(qcc->app_st), (ullong)qcc->nb_sc,
|
qcc_app_st_to_str(qcc->app_st), (ullong)qcc->nb_sc,
|
||||||
(ullong)qcc->nb_hreq, qcc->flags);
|
(ullong)qcc->nb_hreq, qcc->flags);
|
||||||
|
|
||||||
chunk_appendf(msg, " .tx=%llu %llu/%llu bwnd=%llu/%llu",
|
chunk_appendf(msg, " .tx=%llu %llu/%llu",
|
||||||
(ullong)qcc->tx.fc.off_soft, (ullong)qcc->tx.fc.off_real, (ullong)qcc->tx.fc.limit,
|
(ullong)qcc->tx.fc.off_soft, (ullong)qcc->tx.fc.off_real, (ullong)qcc->tx.fc.limit);
|
||||||
(ullong)qcc->tx.buf_in_flight, (ullong)qc->path->cwnd);
|
if (qc)
|
||||||
|
chunk_appendf(msg, " bwnd=%llu/%llu", (ullong)qcc->tx.buf_in_flight, (ullong)qc->path->cwnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmux_dump_qcs_info(struct buffer *msg, const struct qcs *qcs)
|
void qmux_dump_qcs_info(struct buffer *msg, const struct qcs *qcs)
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
#include <haproxy/quic_enc.h>
|
#include <haproxy/quic_enc.h>
|
||||||
#include <haproxy/quic_frame.h>
|
#include <haproxy/quic_frame.h>
|
||||||
#include <haproxy/quic_rx-t.h>
|
#include <haproxy/quic_rx-t.h>
|
||||||
#include <haproxy/quic_tp-t.h>
|
#include <haproxy/quic_tp.h>
|
||||||
#include <haproxy/quic_trace.h>
|
#include <haproxy/quic_trace.h>
|
||||||
#include <haproxy/quic_tx.h>
|
#include <haproxy/quic_tx.h>
|
||||||
#include <haproxy/trace.h>
|
#include <haproxy/trace.h>
|
||||||
@ -1007,6 +1007,43 @@ static int quic_build_handshake_done_frame(unsigned char **pos, const unsigned c
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int quic_build_qs_tp_frame(unsigned char **pos, const unsigned char *end,
|
||||||
|
struct quic_frame *frm, struct quic_conn *conn)
|
||||||
|
{
|
||||||
|
unsigned char *old = *pos;
|
||||||
|
struct buffer buf;
|
||||||
|
|
||||||
|
if (end - *pos < 8)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
buf = b_make((char *)*pos, end - *pos, 0, 0);
|
||||||
|
if (!b_quic_enc_int(&buf, 0, 8))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
//if (!quic_enc_int(pos, end, 0))
|
||||||
|
// return 0;
|
||||||
|
*pos += 8;
|
||||||
|
if (!quic_transport_param_enc_int(pos, end, QUIC_TP_MAX_IDLE_TIMEOUT, 30000))
|
||||||
|
return 0;
|
||||||
|
if (!quic_transport_param_enc_int(pos, end, QUIC_TP_INITIAL_MAX_DATA, 16384))
|
||||||
|
return 0;
|
||||||
|
if (!quic_transport_param_enc_int(pos, end, QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, 16384))
|
||||||
|
return 0;
|
||||||
|
if (!quic_transport_param_enc_int(pos, end, QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, 16384))
|
||||||
|
return 0;
|
||||||
|
if (!quic_transport_param_enc_int(pos, end, QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI, 16384))
|
||||||
|
return 0;
|
||||||
|
if (!quic_transport_param_enc_int(pos, end, QUIC_TP_INITIAL_MAX_STREAMS_BIDI, 100))
|
||||||
|
return 0;
|
||||||
|
if (!quic_transport_param_enc_int(pos, end, QUIC_TP_INITIAL_MAX_STREAMS_UNI, 100))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
buf = b_make((char *)old, 8, 0, 0);
|
||||||
|
b_quic_enc_int(&buf, *pos - 8 - old, 8);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Parse a HANDSHAKE_DONE frame at QUIC layer at <pos> buffer position with <end> as end into <frm> frame.
|
/* Parse a HANDSHAKE_DONE frame at QUIC layer at <pos> buffer position with <end> as end into <frm> frame.
|
||||||
* Always succeed.
|
* Always succeed.
|
||||||
*/
|
*/
|
||||||
@ -1017,6 +1054,17 @@ static int quic_parse_handshake_done_frame(struct quic_frame *frm, struct quic_c
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int quic_parse_qs_tp(struct quic_frame *frm, struct quic_conn *qc,
|
||||||
|
const unsigned char **pos, const unsigned char *end)
|
||||||
|
{
|
||||||
|
struct qf_qs_tp *qs_tp_frm = &frm->qs_tp;
|
||||||
|
uint64_t len;
|
||||||
|
|
||||||
|
quic_dec_int(&len, pos, end);
|
||||||
|
quic_transport_params_decode(&qs_tp_frm->tps, 1, *pos, end);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
struct quic_frame_builder {
|
struct quic_frame_builder {
|
||||||
int (*func)(unsigned char **pos, const unsigned char *end,
|
int (*func)(unsigned char **pos, const unsigned char *end,
|
||||||
struct quic_frame *frm, struct quic_conn *conn);
|
struct quic_frame *frm, struct quic_conn *conn);
|
||||||
@ -1117,11 +1165,25 @@ const struct quic_frame_parser quic_frame_parsers[] = {
|
|||||||
* };
|
* };
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* quic-on-streams transport parameters frame. */
|
||||||
|
const uint64_t QUIC_FT_QS_TP = 0x3f5153300d0a0d0a;
|
||||||
|
const struct quic_frame_parser qf_ft_qs_tp_parser = {
|
||||||
|
.func = quic_parse_qs_tp,
|
||||||
|
.mask = 0,
|
||||||
|
.flags = 0,
|
||||||
|
};
|
||||||
|
const struct quic_frame_builder qf_ft_qs_tp_builder = {
|
||||||
|
.func = quic_build_qs_tp_frame,
|
||||||
|
.mask = 0,
|
||||||
|
.flags = 0,
|
||||||
|
};
|
||||||
|
|
||||||
/* Returns true if frame <type> is supported. */
|
/* Returns true if frame <type> is supported. */
|
||||||
static inline int quic_frame_type_is_known(uint64_t type)
|
static inline int quic_frame_type_is_known(uint64_t type)
|
||||||
{
|
{
|
||||||
/* Complete here for extra frame types greater than QUIC_FT_MAX. */
|
/* Complete here for extra frame types greater than QUIC_FT_MAX. */
|
||||||
return type < QUIC_FT_MAX;
|
return type < QUIC_FT_MAX ||
|
||||||
|
(type == QUIC_FT_QS_TP);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct quic_frame_parser *qf_parser(uint64_t type)
|
static const struct quic_frame_parser *qf_parser(uint64_t type)
|
||||||
@ -1130,6 +1192,8 @@ static const struct quic_frame_parser *qf_parser(uint64_t type)
|
|||||||
return &quic_frame_parsers[type];
|
return &quic_frame_parsers[type];
|
||||||
|
|
||||||
/* Complete here for extra frame types greater than QUIC_FT_MAX. */
|
/* Complete here for extra frame types greater than QUIC_FT_MAX. */
|
||||||
|
if (type == QUIC_FT_QS_TP)
|
||||||
|
return &qf_ft_qs_tp_parser;
|
||||||
|
|
||||||
ABORT_NOW();
|
ABORT_NOW();
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1141,6 +1205,8 @@ const struct quic_frame_builder *qf_builder(uint64_t type)
|
|||||||
return &quic_frame_builders[type];
|
return &quic_frame_builders[type];
|
||||||
|
|
||||||
/* Complete here for extra frame types greater than QUIC_FT_MAX. */
|
/* Complete here for extra frame types greater than QUIC_FT_MAX. */
|
||||||
|
if (type == QUIC_FT_QS_TP)
|
||||||
|
return &qf_ft_qs_tp_builder;
|
||||||
|
|
||||||
ABORT_NOW();
|
ABORT_NOW();
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1175,7 +1241,7 @@ int qc_parse_frm(struct quic_frame *frm, struct quic_rx_packet *pkt,
|
|||||||
}
|
}
|
||||||
|
|
||||||
parser = qf_parser(frm->type);
|
parser = qf_parser(frm->type);
|
||||||
if (!(parser->mask & (1U << pkt->type))) {
|
if (pkt && !(parser->mask & (1U << pkt->type))) {
|
||||||
TRACE_DEVEL("unauthorized frame", QUIC_EV_CONN_PRSFRM, qc, frm);
|
TRACE_DEVEL("unauthorized frame", QUIC_EV_CONN_PRSFRM, qc, frm);
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
@ -1187,7 +1253,8 @@ int qc_parse_frm(struct quic_frame *frm, struct quic_rx_packet *pkt,
|
|||||||
|
|
||||||
TRACE_PROTO("RX frm", QUIC_EV_CONN_PSTRM, qc, frm);
|
TRACE_PROTO("RX frm", QUIC_EV_CONN_PSTRM, qc, frm);
|
||||||
|
|
||||||
pkt->flags |= parser->flags;
|
if (pkt)
|
||||||
|
pkt->flags |= parser->flags;
|
||||||
|
|
||||||
ret = 1;
|
ret = 1;
|
||||||
leave:
|
leave:
|
||||||
@ -1210,7 +1277,7 @@ int qc_build_frm(unsigned char **pos, const unsigned char *end,
|
|||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_BFRM, qc);
|
TRACE_ENTER(QUIC_EV_CONN_BFRM, qc);
|
||||||
builder = qf_builder(frm->type);
|
builder = qf_builder(frm->type);
|
||||||
if (!(builder->mask & (1U << pkt->type))) {
|
if (pkt && !(builder->mask & (1U << pkt->type))) {
|
||||||
/* XXX This it a bug to send an unauthorized frame with such a packet type XXX */
|
/* XXX This it a bug to send an unauthorized frame with such a packet type XXX */
|
||||||
TRACE_ERROR("unauthorized frame", QUIC_EV_CONN_BFRM, qc, frm);
|
TRACE_ERROR("unauthorized frame", QUIC_EV_CONN_BFRM, qc, frm);
|
||||||
BUG_ON(!(builder->mask & (1U << pkt->type)));
|
BUG_ON(!(builder->mask & (1U << pkt->type)));
|
||||||
@ -1227,7 +1294,8 @@ int qc_build_frm(unsigned char **pos, const unsigned char *end,
|
|||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
pkt->flags |= builder->flags;
|
if (pkt)
|
||||||
|
pkt->flags |= builder->flags;
|
||||||
*pos = p;
|
*pos = p;
|
||||||
|
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
@ -394,9 +394,9 @@ static int quic_transport_param_enc_mem(unsigned char **buf, const unsigned char
|
|||||||
/* Encode <val> 64-bits value as variable length integer into <buf>.
|
/* Encode <val> 64-bits value as variable length integer into <buf>.
|
||||||
* Returns 1 if succeeded, 0 if not.
|
* Returns 1 if succeeded, 0 if not.
|
||||||
*/
|
*/
|
||||||
static int quic_transport_param_enc_int(unsigned char **buf,
|
int quic_transport_param_enc_int(unsigned char **buf,
|
||||||
const unsigned char *end,
|
const unsigned char *end,
|
||||||
uint64_t type, uint64_t val)
|
uint64_t type, uint64_t val)
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
@ -598,9 +598,9 @@ int quic_transport_params_encode(unsigned char *buf,
|
|||||||
* or 0 for a client (connection to a haproxy server).
|
* or 0 for a client (connection to a haproxy server).
|
||||||
* Returns 1 if succeeded, 0 if not.
|
* Returns 1 if succeeded, 0 if not.
|
||||||
*/
|
*/
|
||||||
static int quic_transport_params_decode(struct quic_transport_params *p, int server,
|
int quic_transport_params_decode(struct quic_transport_params *p, int server,
|
||||||
const unsigned char *buf,
|
const unsigned char *buf,
|
||||||
const unsigned char *end)
|
const unsigned char *end)
|
||||||
{
|
{
|
||||||
const unsigned char *pos;
|
const unsigned char *pos;
|
||||||
uint64_t type, len = 0;
|
uint64_t type, len = 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user