* crypto[QUIC_PN_SPACE_APP] is the 1-RTT crypto stream.
*/
QUIC_SSTREAM *crypto[QUIC_PN_SPACE_NUM];
-} OSSL_QUIC_TX_PACKETISER_ARGS;
+
+ } OSSL_QUIC_TX_PACKETISER_ARGS;
typedef struct ossl_quic_tx_packetiser_st OSSL_QUIC_TX_PACKETISER;
void ossl_quic_tx_packetiser_free(OSSL_QUIC_TX_PACKETISER *txp);
+/*
+ * When in the closing state we need to maintain a count of received bytes
+ * so that we can limit the number of close connection frames we send.
+ * Refer RFC 9000 s. 10.2.1 Closing Connection State.
+ */
+void ossl_quic_tx_packetiser_record_received_closing_bytes(
+ OSSL_QUIC_TX_PACKETISER *txp, size_t n);
+
/*
* Generates a datagram by polling the various ELs to determine if they want to
* generate any frames, and generating a datagram which coalesces packets for
static int ch_rx(QUIC_CHANNEL *ch)
{
int handled_any = 0;
+ const int closing = ossl_quic_channel_is_closing(ch);
if (!ch->is_server && !ch->have_sent_any_pkt)
/*
if (!ossl_qrx_read_pkt(ch->qrx, &ch->qrx_pkt))
break;
+ /* Track the amount of data received while in the closing state */
+ if (closing)
+ ossl_quic_tx_packetiser_record_received_closing_bytes(
+ ch->txp, ch->qrx_pkt->hdr->len);
+
if (!handled_any)
ch_update_idle(ch);
* When in TERMINATING - CLOSING, generate a CONN_CLOSE frame whenever we
* process one or more incoming packets.
*/
- if (handled_any && ossl_quic_channel_is_closing(ch))
+ if (handled_any && closing)
ch->conn_close_queued = 1;
return 1;
assert(ch->qrx_pkt != NULL);
+ /*
+ * RFC 9000 s. 10.2.1 Closing Connection State:
+ * An endpoint that is closing is not required to process any
+ * received frame.
+ */
if (!ossl_quic_channel_is_active(ch))
- /* Do not process packets once we are terminating. */
return;
if (ossl_quic_pkt_type_is_encrypted(ch->qrx_pkt->hdr->type)) {
OSSL_QUIC_FRAME_CONN_CLOSE conn_close_frame;
+ /*
+ * Counts of the number of bytes received and sent while in the closing
+ * state.
+ */
+ uint64_t closing_bytes_recv;
+ uint64_t closing_bytes_xmit;
+
/* Internal state - packet assembly. */
struct txp_el {
unsigned char *scratch; /* scratch buffer for packet assembly */
ossl_quic_stream_map_update_state(txp->args.qsm, s);
}
+/*
+ * Returns 1 if we can send that many bytes in closing state, 0 otherwise.
+ * Also maintains the bytes sent state if it returns a success.
+ */
+static int try_commit_conn_close(OSSL_QUIC_TX_PACKETISER *txp, size_t n)
+{
+ int res;
+
+ /* We can always send the first connection close frame */
+ if (txp->closing_bytes_recv == 0)
+ return 1;
+
+ /*
+ * RFC 9000 s. 10.2.1 Closing Connection State:
+ * To avoid being used for an amplification attack, such
+ * endpoints MUST limit the cumulative size of packets it sends
+ * to three times the cumulative size of the packets that are
+ * received and attributed to the connection.
+ * and:
+ * An endpoint in the closing state MUST either discard packets
+ * received from an unvalidated address or limit the cumulative
+ * size of packets it sends to an unvalidated address to three
+ * times the size of packets it receives from that address.
+ */
+ res = txp->closing_bytes_xmit + n <= txp->closing_bytes_recv * 3;
+
+ /*
+ * Attribute the bytes to the connection, if we are allowed to send them
+ * and this isn't the first closing frame.
+ */
+ if (res && txp->closing_bytes_recv != 0)
+ txp->closing_bytes_xmit += n;
+ return res;
+}
+
+void ossl_quic_tx_packetiser_record_received_closing_bytes(
+ OSSL_QUIC_TX_PACKETISER *txp, size_t n)
+{
+ txp->closing_bytes_recv += n;
+}
+
static int txp_generate_pre_token(OSSL_QUIC_TX_PACKETISER *txp,
struct txp_pkt *pkt,
int chosen_for_conn_close,
if (a->allow_conn_close && txp->want_conn_close && chosen_for_conn_close) {
WPACKET *wpkt = tx_helper_begin(h);
OSSL_QUIC_FRAME_CONN_CLOSE f, *pf = &txp->conn_close_frame;
+ size_t l;
if (wpkt == NULL)
return 0;
pf->reason_len = 0;
}
- if (ossl_quic_wire_encode_frame_conn_close(wpkt, pf)) {
+ if (ossl_quic_wire_encode_frame_conn_close(wpkt, pf)
+ && WPACKET_get_total_written(wpkt, &l)
+ && try_commit_conn_close(txp, l)) {
if (!tx_helper_commit(h))
return 0;