MINOR: mux_quic: Add QUIC mux layer.

This file has been derived from mux_h2.c removing all h2 parts. At
QUIC mux layer, there must not be any reference to http. This will be the
responsability of the application layer (h3) to open streams handled by the mux.
This commit is contained in:
Frédéric Lécaille 2021-02-18 09:59:01 +01:00 committed by Amaury Denoyelle
parent 5aa4143d6c
commit dfbae766b2
4 changed files with 2767 additions and 0 deletions

View File

@ -0,0 +1,218 @@
/*
* include/haproxy/mux_quic-t.h
* This file containts types for QUIC mux-demux.
*
* Copyright 2021 HAProxy Technologies, Frédéric Lécaille <flecaille@haproxy.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, version 2.1
* exclusively.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_MUX_QUIC_T_H
#define _HAPROXY_MUX_QUIC_T_H
#ifdef USE_QUIC
#ifndef USE_OPENSSL
#error "Must define USE_OPENSSL"
#endif
#include <stdint.h>
#include <haproxy/buf-t.h>
#include <haproxy/connection-t.h>
#include <haproxy/dynbuf-t.h>
#include <import/eb64tree.h>
/* Bit shift to get the stream sub ID for internal use which is obtained
* shifting the stream IDs by this value, knowing that the
* QCS_ID_TYPE_SHIFT less significant bits identify the stream ID
* types (client initiated bidirectional, server initiated bidirectional,
* client initiated unidirectional, server initiated bidirectional).
* Note that there is no reference to such stream sub IDs in the RFC.
*/
#define QCS_ID_TYPE_MASK 0x3
#define QCS_ID_TYPE_SHIFT 2
/* The less significant bit of a stream ID is set for a server initiated stream */
#define QCS_ID_SRV_INTIATOR_BIT 0x1
/* This bit is set for unidirectional streams */
#define QCS_ID_DIR_BIT 0x2
#define QCS_ID_DIR_BIT_SHIFT 1
#define OUQS_SF_TXBUF_MALLOC 0x00000001
#define OUQS_SF_TXBUF_FULL 0x00000002
extern struct pool_head *pool_head_qcs;
/* Stream types */
enum qcs_type {
QCS_CLT_BIDI,
QCS_SRV_BIDI,
QCS_CLT_UNI,
QCS_SRV_UNI,
/* Must be the last one */
QCS_MAX_TYPES,
};
/* 32 buffers: one for the ring's root, rest for the mbuf itself */
#define QCC_MBUF_CNT 32
/* Stream direction types */
enum qcs_dir {
QCS_BIDI = 0,
QCS_UNI = 1,
/* Must be the last one */
QCS_MAX_DIR = 2,
};
/* QUIC connection state, in qcc->st0 */
enum qc_cs {
/* Initial state */
QC_CS_NOERR,
QC_CS_ERROR,
};
/* QUIC connection descriptor */
struct qcc {
struct connection *conn; /* mux state */
enum qc_cs st0; /* connection flags: QC_CF_* */
unsigned int errcode;
uint32_t flags;
/* Stream information, one by direction and by initiator */
struct {
uint64_t max_streams; /* maximum number of concurrent streams */
uint64_t largest_id; /* Largest ID of the open streams */
uint64_t nb_streams; /* Number of open streams */
struct {
uint64_t max_data; /* Maximum number of bytes which may be received */
uint64_t bytes; /* Number of bytes received */
} rx;
struct {
uint64_t max_data; /* Maximum number of bytes which may be sent */
uint64_t bytes; /* Number of bytes sent */
} tx;
} strms[QCS_MAX_TYPES];
struct {
uint64_t max_data; /* Maximum number of bytes which may be received */
uint64_t bytes; /* Number of bytes received */
uint64_t inmux; /* Number of bytes received but not already demultiplexed. */
} rx;
struct {
uint64_t max_data; /* Maximum number of bytes which may be sent */
uint64_t bytes; /* Number of bytes sent */
} tx;
struct eb_root streams_by_id; /* all active streams by their ID */
/* states for the mux direction */
struct buffer mbuf[QCC_MBUF_CNT]; /* mux buffers (ring) */
int timeout; /* idle timeout duration in ticks */
int shut_timeout; /* idle timeout duration in ticks after GOAWAY was sent */
unsigned int nb_cs; /* number of attached conn_streams */
unsigned int stream_cnt; /* total number of streams seen */
struct proxy *proxy; /* the proxy this connection was created for */
struct task *task; /* timeout management task */
struct qc_counters *px_counters; /* quic counters attached to proxy */
struct list send_list; /* list of blocked streams requesting to send */
struct list fctl_list; /* list of streams blocked by connection's fctl */
struct list blocked_list; /* list of streams blocked for other reasons (e.g. sfctl, dep) */
struct buffer_wait buf_wait; /* wait list for buffer allocations */
struct wait_event wait_event; /* To be used if we're waiting for I/Os */
struct mt_list qcs_rxbuf_wlist; /* list of streams waiting for their rxbuf */
void *ctx; /* Application layer context */
const struct qcc_app_ops *app_ops;
};
/* QUIC RX states */
enum qcs_rx_st {
QC_RX_SS_IDLE = 0, // idle
QC_RX_SS_RECV, // receive
QC_RX_SS_SIZE_KNOWN, // stream size known
/* Terminal states */
QC_RX_SS_DATA_RECVD, // all data received
QC_RX_SS_DATA_READ, // app. read all data
QC_RX_SS_RST_RECVD, // reset received
QC_RX_SS_RST_READ, // app. read reset
};
/* QUIC TX states */
enum qcs_tx_st {
QC_TX_SS_IDLE = 0,
QC_TX_SS_READY, // ready
QC_TX_SS_SEND, // send
QC_TX_SS_DATA_SENT, // all data sent
/* Terminal states */
QC_TX_SS_DATA_RECVD, // all data received
QC_TX_SS_RST_SENT, // reset sent
QC_TX_SS_RST_RECVD, // reset received
};
/* QUIC stream flags (32 bit), in qcs->flags */
#define QC_SF_NONE 0x00000000
#define QC_SF_TXBUF_MALLOC 0x00000001 // blocked on lack of TX buffer
/* stream flags indicating the reason the stream is blocked */
#define QC_SF_BLK_MBUSY 0x00000010 // blocked waiting for mux access (transient)
#define QC_SF_BLK_MROOM 0x00000020 // blocked waiting for room in the mux (must be in send list)
#define QC_SF_BLK_MFCTL 0x00000040 // blocked due to mux fctl (must be in fctl list)
#define QC_SF_BLK_SFCTL 0x00000080 // blocked due to stream fctl (must be in blocked list)
#define QC_SF_BLK_ANY 0x000000F0 // any of the reasons above
#define QC_SF_NOTIFIED 0x00000800 // a paused stream was notified to try to send again
#define QC_SF_WANT_SHUTR 0x00008000 // a stream couldn't shutr() (mux full/busy)
#define QC_SF_WANT_SHUTW 0x00010000 // a stream couldn't shutw() (mux full/busy)
#define QC_SF_KILL_CONN 0x00020000 // kill the whole connection with this stream
/* QUIC stream descriptor, describing the stream as it appears in the QUIC_CONN, and as
* it is being processed in the internal HTTP representation (HTX).
*/
struct qcs {
struct conn_stream *cs;
struct session *sess;
struct qcc *qcc;
struct eb64_node by_id; /* place in qcc's streams_by_id */
struct eb_root frms;
uint64_t id; /* stream ID */
uint32_t flags; /* QC_SF_* */
struct {
enum qcs_rx_st st; /* RX state */
uint64_t max_data; /* maximum number of bytes which may be received */
uint64_t offset; /* the current offset of received data */
uint64_t bytes; /* number of bytes received */
struct buffer buf; /* receive buffer, always valid (buf_empty or real buffer) */
} rx;
struct {
enum qcs_tx_st st; /* TX state */
uint64_t max_data; /* maximum number of bytes which may be sent */
uint64_t offset; /* the current offset of data to send */
uint64_t bytes; /* number of bytes sent */
struct buffer buf; /* transmit buffer, always valid (buf_empty or real buffer) */
} tx;
struct wait_event *subs; /* recv wait_event the conn_stream associated is waiting on (via qc_subscribe) */
struct list list; /* To be used when adding in qcc->send_list or qcc->fctl_lsit */
struct tasklet *shut_tl; /* deferred shutdown tasklet, to retry to send an RST after we failed to,
* in case there's no other subscription to do it */
};
/* QUIC application layer operations */
struct qcc_app_ops {
int (*init)(struct qcc *qcc);
int (*attach_ruqs)(struct qcs *qcs, void *ctx);
int (*decode_qcs)(struct qcs *qcs, void *ctx);
int (*finalize)(void *ctx);
};
#endif /* USE_QUIC */
#endif /* _HAPROXY_MUX_QUIC_T_H */

126
include/haproxy/mux_quic.h Normal file
View File

@ -0,0 +1,126 @@
/*
* include/haproxy/mux_quic-t.h
* This file containts prototypes for QUIC mux-demux.
*
* Copyright 2021 HAProxy Technologies, Frédéric Lécaille <flecaille@haproxy.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, version 2.1
* exclusively.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_MUX_QUIC_H
#define _HAPROXY_MUX_QUIC_H
#ifdef USE_QUIC
#ifndef USE_OPENSSL
#error "Must define USE_OPENSSL"
#endif
#include <haproxy/buf-t.h>
#include <haproxy/mux_quic-t.h>
#include <haproxy/obj_type.h>
void quic_mux_transport_params_update(struct qcc *qcc);
struct buffer *qc_get_buf(struct qcc *qcc, struct buffer *bptr);
int qc_cpy_mbuf(struct qcs *qcs, unsigned char *buf, size_t len);
void qc_error(struct qcc *qcc, int err);
struct qcs *qcc_get_stream(struct qcc *qcc, uint64_t id);
struct qcs *bidi_qcs_new(struct qcc *qcc, uint64_t id);
struct qcs *luqs_new(struct qcc *qcc);
struct qcs *ruqs_new(struct qcc *qcc, uint64_t id);
size_t luqs_snd_buf(struct qcs *qcs, struct buffer *buf, size_t count, int flags);
void qcs_release(struct qcs *qcs);
void ruqs_notify_recv(struct qcs *qcs);
/* Return 1 if the stream with <id> as ID attached to <qcc> connection
* has been locally initiated, 0 if not.
*/
static inline int qc_local_stream_id(struct qcc *qcc, uint64_t id)
{
if ((objt_listener(qcc->conn->target) && (id & QCS_ID_SRV_INTIATOR_BIT)) ||
(objt_server(qcc->conn->target) && !(id & QCS_ID_SRV_INTIATOR_BIT)))
return 1;
return 0;
}
/* Return 1 if <qcs> stream has been locally initiated, 0 if not. */
static inline int qcs_local(struct qcs *qcs)
{
if ((objt_listener(qcs->qcc->conn->target) && (qcs->id & QCS_ID_SRV_INTIATOR_BIT)) ||
(objt_server(qcs->qcc->conn->target) && !(qcs->id & QCS_ID_SRV_INTIATOR_BIT)))
return 1;
return 0;
}
/* Return the direction of a stream with <id> as ID. */
static inline enum qcs_dir qcs_id_dir(uint64_t id)
{
return (id & QCS_ID_DIR_BIT) >> QCS_ID_DIR_BIT_SHIFT;
}
/* Return the direction of <qcs> QUIC stream. */
static inline enum qcs_dir qcs_dir(struct qcs *qcs)
{
return (qcs->id & QCS_ID_DIR_BIT) >> QCS_ID_DIR_BIT_SHIFT;
}
static inline enum qcs_type qcs_id_type(uint64_t id)
{
return id & QCS_ID_TYPE_MASK;
}
static inline enum qcs_type qcs_type_from_dir(struct qcc *qcc, enum qcs_dir dir)
{
return (dir << QCS_ID_DIR_BIT_SHIFT) |
(!!objt_listener(qcc->conn->target) ? QCS_ID_SRV_INTIATOR_BIT : 0);
}
static inline int64_t qcc_wnd(struct qcc *qcc)
{
return qcc->tx.max_data - qcc->tx.bytes;
}
/* Return 1 if <qcs> is unidirectional, 0 if not. */
static inline int qcs_uni(struct qcs *qcs)
{
return qcs->id & QCS_ID_DIR_BIT;
}
/* Return 1 if <qcs> is bidirectional, 0 if not. */
static inline int qcs_bidi(struct qcs *qcs)
{
return !qcs_uni(qcs);
}
/* Return the next stream ID with <qcs_type> as type if succeeded, (uint64_t)-1 if not. */
static inline uint64_t qcs_next_id(struct qcc *qcc, enum qcs_type qcs_type)
{
if (qcc->strms[qcs_type].nb_streams + 1 > qcc->strms[qcs_type].max_streams)
return (uint64_t)-1;
return (qcc->strms[qcs_type].nb_streams++ << QCS_ID_TYPE_SHIFT) | qcs_type;
}
static inline void *qcs_new(struct qcc *qcc, uint64_t id)
{
if (id & QCS_ID_DIR_BIT)
return ruqs_new(qcc, id);
else
return bidi_qcs_new(qcc, id);
}
#endif /* USE_QUIC */
#endif /* _HAPROXY_MUX_QUIC_H */

2144
src/mux_quic.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -34,7 +34,9 @@
#include <haproxy/fd.h> #include <haproxy/fd.h>
#include <haproxy/freq_ctr.h> #include <haproxy/freq_ctr.h>
#include <haproxy/global.h> #include <haproxy/global.h>
#include <haproxy/h3.h>
#include <haproxy/log.h> #include <haproxy/log.h>
#include <haproxy/mux_quic.h>
#include <haproxy/pipe.h> #include <haproxy/pipe.h>
#include <haproxy/proxy.h> #include <haproxy/proxy.h>
#include <haproxy/quic_cc.h> #include <haproxy/quic_cc.h>
@ -153,6 +155,8 @@ DECLARE_POOL(pool_head_quic_tx_packet, "quic_tx_packet_pool", sizeof(struct quic
DECLARE_STATIC_POOL(pool_head_quic_rx_crypto_frm, "quic_rx_crypto_frm_pool", sizeof(struct quic_rx_crypto_frm)); DECLARE_STATIC_POOL(pool_head_quic_rx_crypto_frm, "quic_rx_crypto_frm_pool", sizeof(struct quic_rx_crypto_frm));
DECLARE_POOL(pool_head_quic_rx_strm_frm, "quic_rx_strm_frm", sizeof(struct quic_rx_strm_frm));
DECLARE_POOL(pool_head_quic_tx_frm, "quic_tx_frm_pool", sizeof(struct quic_tx_frm)); DECLARE_POOL(pool_head_quic_tx_frm, "quic_tx_frm_pool", sizeof(struct quic_tx_frm));
DECLARE_STATIC_POOL(pool_head_quic_crypto_buf, "quic_crypto_buf_pool", sizeof(struct quic_crypto_buf)); DECLARE_STATIC_POOL(pool_head_quic_crypto_buf, "quic_crypto_buf_pool", sizeof(struct quic_crypto_buf));
@ -1572,6 +1576,277 @@ static inline int qc_provide_cdata(struct quic_enc_level *el,
return 0; return 0;
} }
/* Allocate a new STREAM RX frame from <stream_fm> STREAM frame attached to
* <pkt> RX packet.
* Return it if succeeded, NULL if not.
*/
static inline
struct quic_rx_strm_frm *new_quic_rx_strm_frm(struct quic_stream *stream_frm,
struct quic_rx_packet *pkt)
{
struct quic_rx_strm_frm *frm;
frm = pool_alloc(pool_head_quic_rx_strm_frm);
if (frm) {
frm->offset_node.key = stream_frm->offset;
frm->len = stream_frm->len;
frm->data = stream_frm->data;
frm->pkt = pkt;
}
return frm;
}
/* Retrieve as an ebtree node the stream with <id> as ID, possibly allocates
* several streams, depending on the already open onces.
* Return this node if succeeded, NULL if not.
*/
static struct eb64_node *qcc_get_qcs(struct qcc *qcc, uint64_t id)
{
unsigned int strm_type;
int64_t sub_id;
struct eb64_node *strm_node;
TRACE_ENTER(QUIC_EV_CONN_PSTRM, qcc->conn);
strm_type = id & QCS_ID_TYPE_MASK;
sub_id = id >> QCS_ID_TYPE_SHIFT;
strm_node = NULL;
if (qc_local_stream_id(qcc, id)) {
/* Local streams: this stream must be already opened. */
strm_node = eb64_lookup(&qcc->streams_by_id, id);
if (!strm_node) {
TRACE_PROTO("Unknown stream ID", QUIC_EV_CONN_PSTRM, qcc->conn);
goto out;
}
}
else {
/* Remote streams. */
struct eb_root *strms;
uint64_t largest_id;
enum qcs_type qcs_type;
strms = &qcc->streams_by_id;
qcs_type = qcs_id_type(id);
if (sub_id + 1 > qcc->strms[qcs_type].max_streams) {
TRACE_PROTO("Streams limit reached", QUIC_EV_CONN_PSTRM, qcc->conn);
goto out;
}
/* Note: ->largest_id was initialized with (uint64_t)-1 as value, 0 being a
* correct value.
*/
largest_id = qcc->strms[qcs_type].largest_id;
if (sub_id > (int64_t)largest_id) {
/* RFC: "A stream ID that is used out of order results in all streams
* of that type with lower-numbered stream IDs also being opened".
* So, let's "open" these streams.
*/
int64_t i;
struct qcs *qcs;
qcs = NULL;
for (i = largest_id + 1; i <= sub_id; i++) {
qcs = qcs_new(qcc, (i << QCS_ID_TYPE_SHIFT) | strm_type);
if (!qcs) {
TRACE_PROTO("Could not allocate a new stream",
QUIC_EV_CONN_PSTRM, qcc->conn);
goto out;
}
qcc->strms[qcs_type].largest_id = i;
}
if (qcs)
strm_node = &qcs->by_id;
}
else {
strm_node = eb64_lookup(strms, id);
}
}
TRACE_LEAVE(QUIC_EV_CONN_PSTRM, qcc->conn);
return strm_node;
out:
TRACE_LEAVE(QUIC_EV_CONN_PSTRM, qcc->conn);
return NULL;
}
/* Copy as most as possible STREAM data from <strm_frm> into <strm> stream.
* Returns the number of bytes copied or -1 if failed. Also update <strm_frm> frame
* to reflect the data which have been consumed.
*/
static size_t qc_strm_cpy(struct buffer *buf, struct quic_stream *strm_frm)
{
size_t ret;
ret = 0;
while (strm_frm->len) {
size_t try;
try = b_contig_space(buf);
if (!try)
break;
if (try > strm_frm->len)
try = strm_frm->len;
memcpy(b_tail(buf), strm_frm->data, try);
strm_frm->len -= try;
strm_frm->offset += try;
b_add(buf, try);
ret += try;
}
return ret;
}
/* Handle <strm_frm> bidirectional STREAM frame. Depending on its ID, several
* streams may be open. The data are copied to the stream RX buffer if possible.
* If not, the STREAM frame is stored to be treated again later.
* We rely on the flow control so that not to store too much STREAM frames.
* Return 1 if succeeded, 0 if not.
*/
static int qc_handle_bidi_strm_frm(struct quic_rx_packet *pkt,
struct quic_stream *strm_frm,
struct quic_conn *qc)
{
struct qcs *strm;
struct eb64_node *strm_node, *frm_node;
struct quic_rx_strm_frm *frm;
strm_node = qcc_get_qcs(qc->qcc, strm_frm->id);
if (!strm_node) {
TRACE_PROTO("Stream not found", QUIC_EV_CONN_PSTRM, qc->conn);
return 0;
}
strm = eb64_entry(&strm_node->node, struct qcs, by_id);
frm_node = eb64_lookup(&strm->frms, strm_frm->offset);
/* FIXME: handle the case where this frame overlap others */
if (frm_node) {
TRACE_PROTO("Already existing stream data",
QUIC_EV_CONN_PSTRM, qc->conn);
goto out;
}
if (strm_frm->offset == strm->rx.offset) {
int ret;
if (!qc_get_buf(qc->qcc, &strm->rx.buf))
goto store_frm;
ret = qc_strm_cpy(&strm->rx.buf, strm_frm);
if (ret && qc->qcc->app_ops->decode_qcs(strm, qc->qcc->ctx) == -1) {
TRACE_PROTO("Decoding error", QUIC_EV_CONN_PSTRM);
return 0;
}
strm->rx.offset += ret;
}
if (!strm_frm->len)
goto out;
store_frm:
frm = new_quic_rx_strm_frm(strm_frm, pkt);
if (!frm) {
TRACE_PROTO("Could not alloc RX STREAM frame",
QUIC_EV_CONN_PSTRM, qc->conn);
return 0;
}
eb64_insert(&strm->frms, &frm->offset_node);
quic_rx_packet_refinc(pkt);
out:
return 1;
}
/* Handle <strm_frm> unidirectional STREAM frame. Depending on its ID, several
* streams may be open. The data are copied to the stream RX buffer if possible.
* If not, the STREAM frame is stored to be treated again later.
* We rely on the flow control so that not to store too much STREAM frames.
* Return 1 if succeeded, 0 if not.
*/
static int qc_handle_uni_strm_frm(struct quic_rx_packet *pkt,
struct quic_stream *strm_frm,
struct quic_conn *qc)
{
struct qcs *strm;
struct eb64_node *strm_node, *frm_node;
struct quic_rx_strm_frm *frm;
size_t strm_frm_len;
strm_node = qcc_get_qcs(qc->qcc, strm_frm->id);
if (!strm_node) {
TRACE_PROTO("Stream not found", QUIC_EV_CONN_PSTRM, qc->conn);
return 0;
}
strm = eb64_entry(&strm_node->node, struct qcs, by_id);
frm_node = eb64_lookup(&strm->frms, strm_frm->offset);
/* FIXME: handle the case where this frame overlap others */
if (frm_node) {
TRACE_PROTO("Already existing stream data",
QUIC_EV_CONN_PSTRM, qc->conn);
goto out;
}
strm_frm_len = strm_frm->len;
if (strm_frm->offset == strm->rx.offset) {
int ret;
if (!qc_get_buf(qc->qcc, &strm->rx.buf))
goto store_frm;
/* qc_strm_cpy() will modify the offset, depending on the number
* of bytes copied.
*/
ret = qc_strm_cpy(&strm->rx.buf, strm_frm);
/* Inform the application of the arrival of this new stream */
if (!strm->rx.offset && !qc->qcc->app_ops->attach_ruqs(strm, qc->qcc->ctx)) {
TRACE_PROTO("Could not set an uni-stream", QUIC_EV_CONN_PSTRM, qc->conn);
return 0;
}
if (ret)
ruqs_notify_recv(strm);
strm_frm->offset += ret;
}
/* Take this frame into an account for the stream flow control */
strm->rx.offset += strm_frm_len;
/* It all the data were provided to the application, there is no need to
* store any more inforamtion for it.
*/
if (!strm_frm->len)
goto out;
store_frm:
frm = new_quic_rx_strm_frm(strm_frm, pkt);
if (!frm) {
TRACE_PROTO("Could not alloc RX STREAM frame",
QUIC_EV_CONN_PSTRM, qc->conn);
return 0;
}
eb64_insert(&strm->frms, &frm->offset_node);
quic_rx_packet_refinc(pkt);
out:
return 1;
}
static inline int qc_handle_strm_frm(struct quic_rx_packet *pkt,
struct quic_stream *strm_frm,
struct quic_conn *qc)
{
if (strm_frm->id & QCS_ID_DIR_BIT)
return qc_handle_uni_strm_frm(pkt, strm_frm, qc);
else
return qc_handle_bidi_strm_frm(pkt, strm_frm, qc);
}
/* Parse all the frames of <pkt> QUIC packet for QUIC connection with <ctx> /* Parse all the frames of <pkt> QUIC packet for QUIC connection with <ctx>
* as I/O handler context and <qel> as encryption level. * as I/O handler context and <qel> as encryption level.
* Returns 1 if succeeded, 0 if failed. * Returns 1 if succeeded, 0 if failed.
@ -1666,6 +1941,10 @@ static int qc_parse_pkt_frms(struct quic_rx_packet *pkt, struct quic_conn_ctx *c
goto err; goto err;
} else if (!(stream->id & QUIC_STREAM_FRAME_ID_INITIATOR_BIT)) } else if (!(stream->id & QUIC_STREAM_FRAME_ID_INITIATOR_BIT))
goto err; goto err;
if (!qc_handle_strm_frm(pkt, stream, ctx->conn->qc))
goto err;
break; break;
} }
case QUIC_FT_NEW_CONNECTION_ID: case QUIC_FT_NEW_CONNECTION_ID: