From: Amaury Denoyelle Date: Wed, 16 Nov 2022 10:01:02 +0000 (+0100) Subject: MEDIUM: quic: move receive out of FD handler to quic-conn io-cb X-Git-Tag: v2.8-dev1~187 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7c9fdd9c3a44a67a37a3cc85dcaebdf263604f6b;p=thirdparty%2Fhaproxy.git MEDIUM: quic: move receive out of FD handler to quic-conn io-cb This change is the second part for reception on QUIC connection socket. All operations inside the FD handler has been delayed to quic-conn tasklet via the new function qc_rcv_buf(). With this change, buffer management on reception has been simplified. It is now possible to use a local buffer inside qc_rcv_buf() instead of quic_receiver_buf(). This change is part of quic-conn owned socket implementation. It may be backported to 2.7 after a period of observation. --- diff --git a/include/haproxy/quic_conn.h b/include/haproxy/quic_conn.h index 47e256ef2d..6e4774d443 100644 --- a/include/haproxy/quic_conn.h +++ b/include/haproxy/quic_conn.h @@ -743,6 +743,7 @@ void chunk_frm_appendf(struct buffer *buf, const struct quic_frame *frm); void quic_set_connection_close(struct quic_conn *qc, const struct quic_err err); void quic_set_tls_alert(struct quic_conn *qc, int alert); int quic_set_app_ops(struct quic_conn *qc, const unsigned char *alpn, size_t alpn_len); +int qc_check_dcid(struct quic_conn *qc, unsigned char *dcid, size_t dcid_len); int quic_get_dgram_dcid(unsigned char *buf, const unsigned char *end, unsigned char **dcid, size_t *dcid_len); int qc_send_mux(struct quic_conn *qc, struct list *frms); diff --git a/include/haproxy/quic_sock.h b/include/haproxy/quic_sock.h index 3bb87a2590..f7b0eb5eb2 100644 --- a/include/haproxy/quic_sock.h +++ b/include/haproxy/quic_sock.h @@ -45,6 +45,7 @@ struct task *quic_lstnr_dghdlr(struct task *t, void *ctx, unsigned int state); void quic_lstnr_sock_fd_iocb(int fd); int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t count, int flags); +int qc_rcv_buf(struct quic_conn *qc); /* Set default value for socket as uninitialized. */ static inline void qc_init_fd(struct quic_conn *qc) diff --git a/src/quic_conn.c b/src/quic_conn.c index 65f4629ddb..f86ad4b2a0 100644 --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -4310,6 +4310,9 @@ struct task *quic_conn_app_io_cb(struct task *t, void *context, unsigned int sta TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc); TRACE_STATE("connection handshake state", QUIC_EV_CONN_IO_CB, qc, &qc->state); + if (qc_test_fd(qc)) + qc_rcv_buf(qc); + /* Retranmissions */ if (qc->flags & QUIC_FL_CONN_RETRANS_NEEDED) { TRACE_STATE("retransmission needed", QUIC_EV_CONN_IO_CB, qc); @@ -4393,7 +4396,10 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state) zero_rtt = st < QUIC_HS_ST_COMPLETE && quic_tls_has_rx_sec(eqel) && (!LIST_ISEMPTY(&eqel->rx.pqpkts) || qc_el_rx_pkts(eqel)); - start: + + if (qc_test_fd(qc)) + qc_rcv_buf(qc); + if (st >= QUIC_HS_ST_COMPLETE && qc_el_rx_pkts(&qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE])) { TRACE_DEVEL("remaining Handshake packets", QUIC_EV_CONN_PHPKTS, qc); @@ -7422,6 +7428,37 @@ int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc, return -1; } +/* Check if connection ID of length belongs to local + * CIDs. This can be used to determine if a datagram is addressed to the right + * connection instance. + * + * Returns a boolean value. + */ +int qc_check_dcid(struct quic_conn *qc, unsigned char *dcid, size_t dcid_len) +{ + struct ebmb_node *node; + struct quic_connection_id *id; + + /* For ODCID, address is concatenated to it after qc.odcid.len so this + * comparison is safe. + */ + if ((qc->scid.len == dcid_len && + memcmp(qc->scid.data, dcid, dcid_len) == 0) || + (qc->odcid.len == dcid_len && + memcmp(qc->odcid.data, dcid, dcid_len)) == 0) { + return 1; + } + + node = ebmb_lookup(&quic_dghdlrs[tid].cids, dcid, dcid_len); + if (node) { + id = ebmb_entry(node, struct quic_connection_id, node); + if (qc == id->qc) + return 1; + } + + return 0; +} + /* Retrieve the DCID from a QUIC datagram or packet with as first octet. * Returns 1 if succeeded, 0 if not. */ diff --git a/src/quic_sock.c b/src/quic_sock.c index f1207b7502..5b10d8a8ad 100644 --- a/src/quic_sock.c +++ b/src/quic_sock.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -487,84 +488,14 @@ void quic_lstnr_sock_fd_iocb(int fd) /* FD-owned quic-conn socket callback. */ static void quic_conn_sock_fd_iocb(int fd) { - struct quic_conn *qc; + struct quic_conn *qc = fdtab[fd].owner; - struct sockaddr_storage saddr = {0}, daddr = {0}; - struct quic_receiver_buf *rxbuf; - struct quic_transport_params *params; - struct quic_dgram *new_dgram; - struct buffer *buf; - size_t max_sz; - size_t cspace; - unsigned char *dgram_buf; - struct listener *l; - ssize_t ret = 0; - - qc = fdtab[fd].owner; - l = qc->li; TRACE_ENTER(QUIC_EV_CONN_RCV, qc); - new_dgram = NULL; - rxbuf = MT_LIST_POP(&l->rx.rxbuf_list, typeof(rxbuf), rxbuf_el); - if (!rxbuf) - return; - - buf = &rxbuf->buf; - new_dgram = quic_rxbuf_purge_dgrams(rxbuf); - - params = &l->bind_conf->quic_params; - max_sz = params->max_udp_payload_size; - cspace = b_contig_space(buf); - if (cspace < max_sz) { - struct quic_dgram *dgram; - - /* Do no mark as full, and do not try to consume it - * if the contiguous remaining space is not at the end - */ - if (b_tail(buf) + cspace < b_wrap(buf)) - goto end; - - /* Allocate a fake datagram, without data to locate - * the end of the RX buffer (required during purging). - */ - dgram = pool_alloc(pool_head_quic_dgram); - if (!dgram) - goto end; - - /* Initialize only the useful members of this fake datagram. */ - dgram->buf = NULL; - dgram->len = cspace; - /* Append this datagram only to the RX buffer list. It will - * not be treated by any datagram handler. - */ - LIST_APPEND(&rxbuf->dgram_list, &dgram->recv_list); - - /* Consume the remaining space */ - b_add(buf, cspace); - if (b_contig_space(buf) < max_sz) - goto end; - } - - dgram_buf = (unsigned char *)b_tail(buf); - ret = quic_recv(qc->fd, dgram_buf, max_sz, - (struct sockaddr *)&saddr, sizeof(saddr), - (struct sockaddr *)&daddr, sizeof(daddr), - get_net_port(&qc->local_addr)); - if (ret <= 0) - goto end; - - b_add(buf, ret); - if (!quic_lstnr_dgram_dispatch(dgram_buf, ret, l, &qc->peer_addr, &qc->local_addr, - new_dgram, &rxbuf->dgram_list)) { - b_del(buf, ret); - } - new_dgram = NULL; + tasklet_wakeup_after(NULL, qc->wait_event.tasklet); + fd_stop_recv(fd); - end: - pool_free(pool_head_quic_dgram, new_dgram); - MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->rxbuf_el); TRACE_LEAVE(QUIC_EV_CONN_RCV, qc); - return; } /* Send a datagram stored into buffer with as size. @@ -627,6 +558,96 @@ int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz, return 0; } +/* Receive datagram on FD-owned socket. + * + * Returns the total number of bytes read or a negative value on error. + */ +int qc_rcv_buf(struct quic_conn *qc) +{ + struct sockaddr_storage saddr = {0}, daddr = {0}; + struct quic_transport_params *params; + struct quic_dgram *new_dgram = NULL; + struct buffer buf = BUF_NULL; + size_t max_sz; + unsigned char *dgram_buf; + struct listener *l; + ssize_t ret = 0; + + /* Do not call this if quic-conn FD is uninitialized. */ + BUG_ON(qc->fd < 0); + + TRACE_ENTER(QUIC_EV_CONN_RCV, qc); + l = qc->li; + + params = &l->bind_conf->quic_params; + max_sz = params->max_udp_payload_size; + + do { + if (!b_alloc(&buf)) + break; /* TODO subscribe for memory again available. */ + + b_reset(&buf); + BUG_ON(b_contig_space(&buf) < max_sz); + + /* Allocate datagram on first loop or after requeuing. */ + if (!new_dgram && !(new_dgram = pool_alloc(pool_head_quic_dgram))) + break; /* TODO subscribe for memory again available. */ + + dgram_buf = (unsigned char *)b_tail(&buf); + ret = quic_recv(qc->fd, dgram_buf, max_sz, + (struct sockaddr *)&saddr, sizeof(saddr), + (struct sockaddr *)&daddr, sizeof(daddr), + get_net_port(&qc->local_addr)); + if (ret <= 0) { + /* Subscribe FD for future reception. */ + fd_want_recv(qc->fd); + break; + } + + b_add(&buf, ret); + + new_dgram->buf = dgram_buf; + new_dgram->len = ret; + new_dgram->dcid_len = 0; + new_dgram->dcid = NULL; + new_dgram->saddr = saddr; + new_dgram->daddr = daddr; + new_dgram->qc = NULL; /* set later via quic_dgram_parse() */ + + TRACE_DEVEL("read datagram", QUIC_EV_CONN_RCV, qc, new_dgram); + + if (!quic_get_dgram_dcid(new_dgram->buf, + new_dgram->buf + new_dgram->len, + &new_dgram->dcid, &new_dgram->dcid_len)) { + continue; + } + + if (!qc_check_dcid(qc, new_dgram->dcid, new_dgram->dcid_len)) { + /* Datagram received by error on the connection FD, dispatch it + * to its associated quic-conn. + * + * TODO count redispatch datagrams. + */ + TRACE_STATE("wrong datagram on quic-conn socket, prepare to requeue it", QUIC_EV_CONN_RCV, qc); + ABORT_NOW(); + } + + quic_dgram_parse(new_dgram, qc, qc->li); + /* A datagram must always be consumed after quic_parse_dgram(). */ + BUG_ON(new_dgram->buf); + } while (ret > 0); + + pool_free(pool_head_quic_dgram, new_dgram); + + if (b_size(&buf)) { + b_free(&buf); + offer_buffers(NULL, 1); + } + + TRACE_LEAVE(QUIC_EV_CONN_RCV, qc); + return ret; +} + /* Allocate a socket file-descriptor specific for QUIC connection . * Endpoint addresses are specified by the two following arguments : is * the local address and is the remote one.