mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-05-04 12:41:00 +02:00
MEDIUM: quic: Send CONNECTION_CLOSE packets from a dedicated buffer.
Add a new pool <pool_head_quic_cc_buf> for buffer used when building datagram wich CONNECTION_CLOSE frames inside with QUIC_MIN_CC_PKTSIZE(128) as minimum size. Add ->cc_buf_area to quic_conn struct to store such buffers. Add ->cc_dgram_len to store the size of the "connection close" datagrams and ->cc_buf a buffer struct to be used with ->cc_buf_area as ->area member value. Implement qc_get_txb() to be called in place of qc_txb_alloc() to allocate a struct "quic_cc_buf" buffer when the connection needs an immediate close or a buffer struct if not. Modify qc_prep_hptks() and qc_prep_app_pkts() to allow them to use such "quic_cc_buf" buffer when an immediate close is required.
This commit is contained in:
parent
f7ab5918d1
commit
dc9b8e1f27
@ -522,6 +522,11 @@ struct quic_conn {
|
||||
struct quic_transport_params params;
|
||||
/* Send buffer used to write datagrams. */
|
||||
struct buffer buf;
|
||||
/* Send buffer used to send a "connection close" datagram . */
|
||||
struct buffer cc_buf;
|
||||
char *cc_buf_area;
|
||||
/* Length of the "connection close" datagram. */
|
||||
size_t cc_dgram_len;
|
||||
} tx;
|
||||
struct {
|
||||
/* Transport parameters the peer will receive */
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
#ifndef _HAPROXY_TX_T_H
|
||||
#define _HAPROXY_TX_T_H
|
||||
|
||||
#define QUIC_MIN_CC_PKTSIZE 128
|
||||
#define QUIC_DGRAM_HEADLEN (sizeof(uint16_t) + sizeof(void *))
|
||||
#define QUIC_MAX_CC_BUFSIZE (2 * (QUIC_MIN_CC_PKTSIZE + QUIC_DGRAM_HEADLEN))
|
||||
|
||||
extern struct pool_head *pool_head_quic_tx_packet;
|
||||
extern struct pool_head *pool_head_quic_cc_buf;
|
||||
|
||||
/* Flag a sent packet as being an ack-eliciting packet. */
|
||||
#define QUIC_FL_TX_PACKET_ACK_ELICITING (1UL << 0)
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
struct buffer *qc_txb_alloc(struct quic_conn *qc);
|
||||
void qc_txb_release(struct quic_conn *qc);
|
||||
int qc_purge_txbuf(struct quic_conn *qc, struct buffer *buf);
|
||||
struct buffer *qc_get_txb(struct quic_conn *qc);
|
||||
|
||||
int qc_need_sending(struct quic_conn *qc, struct quic_enc_level *qel);
|
||||
int qc_prep_hpkts(struct quic_conn *qc, struct buffer *buf, struct list *qels);
|
||||
|
||||
@ -793,7 +793,7 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
|
||||
}
|
||||
}
|
||||
|
||||
buf = qc_txb_alloc(qc);
|
||||
buf = qc_get_txb(qc);
|
||||
if (!buf)
|
||||
goto out;
|
||||
|
||||
@ -1136,6 +1136,9 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
qc->bytes.tx = qc->bytes.prep = 0;
|
||||
memset(&qc->tx.params, 0, sizeof(qc->tx.params));
|
||||
qc->tx.buf = BUF_NULL;
|
||||
qc->tx.cc_buf = BUF_NULL;
|
||||
qc->tx.cc_buf_area = NULL;
|
||||
qc->tx.cc_dgram_len = 0;
|
||||
/* RX part. */
|
||||
qc->bytes.rx = 0;
|
||||
memset(&qc->rx.params, 0, sizeof(qc->rx.params));
|
||||
@ -1320,6 +1323,7 @@ void quic_conn_release(struct quic_conn *qc)
|
||||
quic_conn_prx_cntrs_update(qc);
|
||||
pool_free(pool_head_quic_conn_rxbuf, qc->rx.buf.area);
|
||||
qc->rx.buf.area = NULL;
|
||||
pool_free(pool_head_quic_cc_buf, qc->tx.cc_buf_area);
|
||||
pool_free(pool_head_quic_conn, qc);
|
||||
qc = NULL;
|
||||
|
||||
|
||||
153
src/quic_tx.c
153
src/quic_tx.c
@ -24,6 +24,7 @@
|
||||
#define TRACE_SOURCE &trace_quic
|
||||
|
||||
DECLARE_POOL(pool_head_quic_tx_packet, "quic_tx_packet", sizeof(struct quic_tx_packet));
|
||||
DECLARE_POOL(pool_head_quic_cc_buf, "quic_cc_buf", QUIC_MAX_CC_BUFSIZE);
|
||||
|
||||
static struct quic_tx_packet *qc_build_pkt(unsigned char **pos, const unsigned char *buf_end,
|
||||
struct quic_enc_level *qel, struct quic_tls_ctx *ctx,
|
||||
@ -368,6 +369,37 @@ void qc_txb_release(struct quic_conn *qc)
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the TX buffer dedicated to the "connection close" datagram to be built
|
||||
* if an immediate close is required after having allocated it or directly
|
||||
* allocate a TX buffer if an immediate close is not required.
|
||||
*/
|
||||
struct buffer *qc_get_txb(struct quic_conn *qc)
|
||||
{
|
||||
struct buffer *buf;
|
||||
|
||||
if (qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE) {
|
||||
TRACE_PROTO("Immediate close required", QUIC_EV_CONN_PHPKTS, qc);
|
||||
buf = &qc->tx.cc_buf;
|
||||
if (b_is_null(buf)) {
|
||||
qc->tx.cc_buf_area = pool_alloc(pool_head_quic_cc_buf);
|
||||
if (!qc->tx.cc_buf_area)
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* In every case, initialize ->tx.cc_buf */
|
||||
qc->tx.cc_buf = b_make(qc->tx.cc_buf_area, QUIC_MAX_CC_BUFSIZE, 0, 0);
|
||||
}
|
||||
else {
|
||||
buf = qc_txb_alloc(qc);
|
||||
if (!buf)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return buf;
|
||||
err:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Commit a datagram payload written into <buf> of length <length>. <first_pkt>
|
||||
* must contains the address of the first packet stored in the payload.
|
||||
*
|
||||
@ -438,27 +470,27 @@ static int qc_may_build_pkt(struct quic_conn *qc, struct list *frms,
|
||||
static int qc_prep_app_pkts(struct quic_conn *qc, struct buffer *buf,
|
||||
struct list *frms)
|
||||
{
|
||||
int ret = -1;
|
||||
int ret = -1, cc;
|
||||
struct quic_enc_level *qel;
|
||||
unsigned char *end, *pos;
|
||||
struct quic_tx_packet *pkt;
|
||||
size_t total;
|
||||
/* Each datagram is prepended with its length followed by the address
|
||||
* of the first packet in the datagram.
|
||||
*/
|
||||
const size_t dg_headlen = sizeof(uint16_t) + sizeof(pkt);
|
||||
|
||||
TRACE_ENTER(QUIC_EV_CONN_PHPKTS, qc);
|
||||
|
||||
qel = qc->ael;
|
||||
total = 0;
|
||||
pos = (unsigned char *)b_tail(buf);
|
||||
while (b_contig_space(buf) >= (int)qc->path->mtu + dg_headlen) {
|
||||
int err, probe, cc, must_ack;
|
||||
cc = qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE;
|
||||
/* Each datagram is prepended with its length followed by the address
|
||||
* of the first packet in the datagram (QUIC_DGRAM_HEADLEN).
|
||||
*/
|
||||
while ((!cc && b_contig_space(buf) >= (int)qc->path->mtu + QUIC_DGRAM_HEADLEN) ||
|
||||
(cc && b_contig_space(buf) >= QUIC_MIN_CC_PKTSIZE + QUIC_DGRAM_HEADLEN)) {
|
||||
int err, probe, must_ack;
|
||||
|
||||
TRACE_PROTO("TX prep app pkts", QUIC_EV_CONN_PHPKTS, qc, qel, frms);
|
||||
probe = 0;
|
||||
cc = qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE;
|
||||
/* We do not probe if an immediate close was asked */
|
||||
if (!cc)
|
||||
probe = qel->pktns->tx.pto_probe;
|
||||
@ -467,8 +499,11 @@ static int qc_prep_app_pkts(struct quic_conn *qc, struct buffer *buf,
|
||||
break;
|
||||
|
||||
/* Leave room for the datagram header */
|
||||
pos += dg_headlen;
|
||||
if (!quic_peer_validated_addr(qc) && qc_is_listener(qc)) {
|
||||
pos += QUIC_DGRAM_HEADLEN;
|
||||
if (cc) {
|
||||
end = pos + QUIC_MIN_CC_PKTSIZE;
|
||||
}
|
||||
else if (!quic_peer_validated_addr(qc) && qc_is_listener(qc)) {
|
||||
end = pos + QUIC_MIN((uint64_t)qc->path->mtu, quic_may_send_bytes(qc));
|
||||
}
|
||||
else {
|
||||
@ -502,9 +537,16 @@ static int qc_prep_app_pkts(struct quic_conn *qc, struct buffer *buf,
|
||||
|
||||
/* Write datagram header. */
|
||||
qc_txb_store(buf, pkt->len, pkt);
|
||||
/* Build only one datagram when an immediate close is required. */
|
||||
if (cc)
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
if (total && cc) {
|
||||
BUG_ON(buf != &qc->tx.cc_buf);
|
||||
qc->tx.cc_dgram_len = total;
|
||||
}
|
||||
ret = total;
|
||||
leave:
|
||||
TRACE_LEAVE(QUIC_EV_CONN_PHPKTS, qc);
|
||||
@ -825,9 +867,9 @@ int qc_send_app_pkts(struct quic_conn *qc, struct list *frms)
|
||||
|
||||
TRACE_ENTER(QUIC_EV_CONN_TXPKT, qc);
|
||||
|
||||
buf = qc_txb_alloc(qc);
|
||||
buf = qc_get_txb(qc);
|
||||
if (!buf) {
|
||||
TRACE_ERROR("buffer allocation failed", QUIC_EV_CONN_TXPKT, qc);
|
||||
TRACE_ERROR("could not get a buffer", QUIC_EV_CONN_TXPKT, qc);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -947,6 +989,37 @@ static inline int qc_qel_is_head(struct quic_enc_level *qel, struct list *l,
|
||||
return !retrans ? &qel->list == l : &qel->retrans == l;
|
||||
}
|
||||
|
||||
/* Select <*tls_ctx>, <*frms> and <*ver> for the encryption level <qel> of <qc> QUIC
|
||||
* connection, depending on its state, especially the negotiated version and if
|
||||
* retransmissions are required. If this the case <qels> is the list of encryption
|
||||
* levels to used, or NULL if no retransmissions are required.
|
||||
* Never fails.
|
||||
*/
|
||||
static inline void qc_select_tls_frms_ver(struct quic_conn *qc,
|
||||
struct quic_enc_level *qel,
|
||||
struct quic_tls_ctx **tls_ctx,
|
||||
struct list **frms,
|
||||
const struct quic_version **ver,
|
||||
struct list *qels)
|
||||
{
|
||||
if (qc->negotiated_version) {
|
||||
*ver = qc->negotiated_version;
|
||||
if (qel == qc->iel)
|
||||
*tls_ctx = qc->nictx;
|
||||
else
|
||||
*tls_ctx = &qel->tls_ctx;
|
||||
}
|
||||
else {
|
||||
*ver = qc->original_version;
|
||||
*tls_ctx = &qel->tls_ctx;
|
||||
}
|
||||
|
||||
if (!qels)
|
||||
*frms = &qel->pktns->tx.frms;
|
||||
else
|
||||
*frms = qel->retrans_frms;
|
||||
}
|
||||
|
||||
/* Prepare as much as possible QUIC datagrams/packets for sending from <qels>
|
||||
* list of encryption levels. Several packets can be coalesced into a single
|
||||
* datagram. The result is written into <buf>. Note that if <qels> is NULL,
|
||||
@ -961,10 +1034,9 @@ static inline int qc_qel_is_head(struct quic_enc_level *qel, struct list *l,
|
||||
*/
|
||||
int qc_prep_hpkts(struct quic_conn *qc, struct buffer *buf, struct list *qels)
|
||||
{
|
||||
int ret, retrans, padding;
|
||||
int ret, cc, retrans, padding;
|
||||
struct quic_tx_packet *first_pkt, *prv_pkt;
|
||||
unsigned char *end, *pos;
|
||||
const size_t dg_headlen = sizeof(uint16_t) + sizeof(first_pkt);
|
||||
uint16_t dglen;
|
||||
size_t total;
|
||||
struct list *qel_list;
|
||||
@ -977,6 +1049,7 @@ int qc_prep_hpkts(struct quic_conn *qc, struct buffer *buf, struct list *qels)
|
||||
BUG_ON_HOT(buf->head || buf->data);
|
||||
|
||||
ret = -1;
|
||||
cc = qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE;
|
||||
retrans = !!qels;
|
||||
padding = 0;
|
||||
first_pkt = prv_pkt = NULL;
|
||||
@ -998,43 +1071,35 @@ int qc_prep_hpkts(struct quic_conn *qc, struct buffer *buf, struct list *qels)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (qc->negotiated_version) {
|
||||
ver = qc->negotiated_version;
|
||||
if (qel == qc->iel)
|
||||
tls_ctx = qc->nictx;
|
||||
else
|
||||
tls_ctx = &qel->tls_ctx;
|
||||
}
|
||||
else {
|
||||
ver = qc->original_version;
|
||||
tls_ctx = &qel->tls_ctx;
|
||||
}
|
||||
|
||||
if (!qels)
|
||||
frms = &qel->pktns->tx.frms;
|
||||
else
|
||||
frms = qel->retrans_frms;
|
||||
qc_select_tls_frms_ver(qc, qel, &tls_ctx, &frms, &ver, qels);
|
||||
|
||||
next_qel = qc_next_qel(qel, retrans);
|
||||
next_frms = qc_qel_is_head(next_qel, qel_list, retrans) ? NULL :
|
||||
!qels ? &next_qel->pktns->tx.frms : next_qel->retrans_frms;
|
||||
|
||||
/* Build as much as datagrams at <qel> encryption level. */
|
||||
while (b_contig_space(buf) >= (int)qc->path->mtu + dg_headlen || prv_pkt) {
|
||||
int err, probe, cc, must_ack;
|
||||
/* Build as much as datagrams at <qel> encryption level.
|
||||
* Each datagram is prepended with its length followed by the address
|
||||
* of the first packet in the datagram (QUIC_DGRAM_HEADLEN).
|
||||
*/
|
||||
while ((!cc && b_contig_space(buf) >= (int)qc->path->mtu + QUIC_DGRAM_HEADLEN) ||
|
||||
(cc && b_contig_space(buf) >= QUIC_MIN_CC_PKTSIZE + QUIC_DGRAM_HEADLEN) || prv_pkt) {
|
||||
int err, probe, must_ack;
|
||||
enum quic_pkt_type pkt_type;
|
||||
struct quic_tx_packet *cur_pkt;
|
||||
|
||||
TRACE_PROTO("TX prep pkts", QUIC_EV_CONN_PHPKTS, qc, qel);
|
||||
probe = 0;
|
||||
cc = qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE;
|
||||
/* We do not probe if an immediate close was asked */
|
||||
if (!cc)
|
||||
probe = qel->pktns->tx.pto_probe;
|
||||
|
||||
if (!qc_may_build_pkt(qc, frms, qel, cc, probe, &must_ack)) {
|
||||
if (prv_pkt && qc_qel_is_head(next_qel, qel_list, retrans))
|
||||
if (prv_pkt && qc_qel_is_head(next_qel, qel_list, retrans)) {
|
||||
qc_txb_store(buf, dglen, first_pkt);
|
||||
/* Build only one datagram when an immediate close is required. */
|
||||
if (cc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
TRACE_DEVEL("next encryption level", QUIC_EV_CONN_PHPKTS, qc);
|
||||
break;
|
||||
@ -1042,8 +1107,11 @@ int qc_prep_hpkts(struct quic_conn *qc, struct buffer *buf, struct list *qels)
|
||||
|
||||
if (!prv_pkt) {
|
||||
/* Leave room for the datagram header */
|
||||
pos += dg_headlen;
|
||||
if (!quic_peer_validated_addr(qc) && qc_is_listener(qc)) {
|
||||
pos += QUIC_DGRAM_HEADLEN;
|
||||
if (cc) {
|
||||
end = pos + QUIC_MIN_CC_PKTSIZE;
|
||||
}
|
||||
else if (!quic_peer_validated_addr(qc) && qc_is_listener(qc)) {
|
||||
end = pos + QUIC_MIN((uint64_t)qc->path->mtu, quic_may_send_bytes(qc));
|
||||
}
|
||||
else {
|
||||
@ -1129,6 +1197,9 @@ int qc_prep_hpkts(struct quic_conn *qc, struct buffer *buf, struct list *qels)
|
||||
}
|
||||
else {
|
||||
qc_txb_store(buf, dglen, first_pkt);
|
||||
/* Build only one datagram when an immediate close is required. */
|
||||
if (cc)
|
||||
goto out;
|
||||
first_pkt = NULL;
|
||||
dglen = 0;
|
||||
padding = 0;
|
||||
@ -1141,6 +1212,12 @@ int qc_prep_hpkts(struct quic_conn *qc, struct buffer *buf, struct list *qels)
|
||||
}
|
||||
|
||||
out:
|
||||
if (cc && total) {
|
||||
BUG_ON(buf != &qc->tx.cc_buf);
|
||||
BUG_ON(dglen != total);
|
||||
qc->tx.cc_dgram_len = dglen;
|
||||
}
|
||||
|
||||
ret = total;
|
||||
leave:
|
||||
TRACE_LEAVE(QUIC_EV_CONN_PHPKTS, qc);
|
||||
@ -1157,7 +1234,7 @@ int qc_send_hdshk_pkts(struct quic_conn *qc, int old_data,
|
||||
struct quic_enc_level *qel1, struct quic_enc_level *qel2)
|
||||
{
|
||||
int ret, status = 0;
|
||||
struct buffer *buf = qc_txb_alloc(qc);
|
||||
struct buffer *buf = qc_get_txb(qc);
|
||||
struct list qels = LIST_HEAD_INIT(qels);
|
||||
|
||||
TRACE_ENTER(QUIC_EV_CONN_TXPKT, qc);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user