#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,
}
}
+/* 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.
*
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;
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 {
/* 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);
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;
}
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,
*/
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;
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;
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;
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 {
}
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;
}
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);
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);