]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: quic: report closing state for the MUX
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 6 Apr 2022 08:28:43 +0000 (10:28 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 7 Apr 2022 08:37:45 +0000 (10:37 +0200)
Define a new API to notify the MUX from the quic-conn when the
connection is about to be closed. This happens in the following cases :
- on idle timeout
- on CONNECTION_CLOSE emission or reception

The MUX wake callback is called on these conditions. The quic-conn
QUIC_FL_NOTIFY_CLOSE is set to only report once. On the MUX side,
connection flags CO_FL_SOCK_RD_SH|CO_FL_SOCK_WR_SH are set to interrupt
future emission/reception.

This patch is the counterpart to
  "MEDIUM: mux-quic: report CO_FL_ERROR on send".
Now the quic-conn is able to report its closing, which may be translated
by the MUX into a CO_FL_ERROR on the connection for the upper layer.
This allows the MUX to properly react to the QUIC closing mechanism for
both idle-timeout and closing/draining states.

include/haproxy/xprt_quic-t.h
include/haproxy/xprt_quic.h
src/mux_quic.c
src/xprt_quic.c

index fbe2ff7735bf3c1535d9bf278907aedeeeb4fb4a..e2999a571e53a093bfbee766047b1f00e8719531 100644 (file)
@@ -664,6 +664,7 @@ enum qc_mux_state {
 #define QUIC_FL_CONN_LISTENER                    (1U << 3)
 #define QUIC_FL_CONN_ACCEPT_REGISTERED           (1U << 4)
 #define QUIC_FL_CONN_IDLE_TIMER_RESTARTED_AFTER_READ (1U << 6)
+#define QUIC_FL_CONN_NOTIFY_CLOSE                (1U << 27) /* MUX notified about quic-conn imminent closure (idle-timeout or CONNECTION_CLOSE emission/reception) */
 #define QUIC_FL_CONN_EXP_TIMER                   (1U << 28) /* timer has expired, quic-conn can be freed */
 #define QUIC_FL_CONN_CLOSING                     (1U << 29)
 #define QUIC_FL_CONN_DRAINING                    (1U << 30)
index 9bf71e9064d7b9e93a0ac3c4c314c839f85854b9..94488b04a74042c917b09da5835fc7f5d56ec478 100644 (file)
@@ -1251,5 +1251,7 @@ int qc_send_app_pkts(struct quic_conn *qc, struct list *frms);
 struct qc_stream_desc *qc_stream_desc_new(uint64_t id, void *ctx);
 void qc_stream_desc_release(struct qc_stream_desc *stream, struct quic_conn *qc);
 
+void qc_notify_close(struct quic_conn *qc);
+
 #endif /* USE_QUIC */
 #endif /* _HAPROXY_XPRT_QUIC_H */
index cdac9a6f552e5ac6c0e3a3c1ae894a40aaf53e29..8b85c3314a6bc6c5b6bd14a407cdfa9dcf70c24e 100644 (file)
@@ -1255,6 +1255,9 @@ static int qc_wake(struct connection *conn)
        if (unlikely(prx->flags & (PR_FL_DISABLED|PR_FL_STOPPED)))
                goto release;
 
+       if (conn->qc->flags & QUIC_FL_CONN_NOTIFY_CLOSE)
+               qcc->conn->flags |= (CO_FL_SOCK_RD_SH|CO_FL_SOCK_WR_SH);
+
        qc_send(qcc);
 
        qc_wake_some_streams(qcc);
index 65414fde0cb37178ac0897779c852b3e844f2ddc..9dd280a18696e1656f2f9d4e25e0a0966353af51 100644 (file)
@@ -2590,6 +2590,7 @@ static int qc_parse_pkt_frms(struct quic_rx_packet *pkt, struct ssl_sock_ctx *ct
                                 */
                                qc_idle_timer_do_rearm(qc);
                                qc->flags |= QUIC_FL_CONN_DRAINING|QUIC_FL_CONN_IMMEDIATE_CLOSE;
+                               qc_notify_close(qc);
                        }
                        break;
                case QUIC_FT_HANDSHAKE_DONE:
@@ -3045,6 +3046,8 @@ int qc_send_ppkts(struct qring *qr, struct ssl_sock_ctx *ctx)
                        if (!(qc->flags & QUIC_FL_CONN_CLOSING) &&
                            (pkt->flags & QUIC_FL_TX_PACKET_CC)) {
                                qc->flags |= QUIC_FL_CONN_CLOSING;
+                               qc_notify_close(qc);
+
                                /* RFC 9000 10.2. Immediate Close:
                                 * The closing and draining connection states exist to ensure
                                 * that connections close cleanly and that delayed or reordered
@@ -4115,6 +4118,11 @@ static struct task *qc_idle_timer_task(struct task *t, void *ctx, unsigned int s
 {
        struct quic_conn *qc = ctx;
 
+       /* Notify the MUX before settings QUIC_FL_CONN_EXP_TIMER or the MUX
+        * might free the quic-conn too early via quic_close().
+        */
+       qc_notify_close(qc);
+
        /* If the MUX is still alive, keep the quic-conn. The MUX is
         * responsible to call quic_close to release it.
         */
@@ -5925,6 +5933,19 @@ void qc_stream_desc_release(struct qc_stream_desc *stream,
                eb64_insert(&qc->streams_by_id, &stream->by_id);
 }
 
+/* Notify the MUX layer if alive about an imminent close of <qc>. */
+void qc_notify_close(struct quic_conn *qc)
+{
+       if (qc->flags & QUIC_FL_CONN_NOTIFY_CLOSE)
+               return;
+
+       qc->flags |= QUIC_FL_CONN_NOTIFY_CLOSE;
+
+       /* wake up the MUX */
+       if (qc->mux_state == QC_MUX_READY && qc->conn->mux->wake)
+               qc->conn->mux->wake(qc->conn);
+}
+
 /* Function to automatically activate QUIC traces on stdout.
  * Activated via the compilation flag -DENABLE_QUIC_STDOUT_TRACES.
  * Main use for now is in the docker image for QUIC interop testing.