2020-11-06 09:49:49 -05:00
|
|
|
/*
|
|
|
|
|
* QUIC socket management.
|
|
|
|
|
*
|
2022-03-02 16:33:39 -05:00
|
|
|
* Copyright 2020 HAProxy Technologies, Frederic Lecaille <flecaille@haproxy.com>
|
2020-11-06 09:49:49 -05:00
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
2022-09-23 11:15:58 -04:00
|
|
|
#define _GNU_SOURCE /* required for struct in6_pktinfo */
|
2020-11-06 09:49:49 -05:00
|
|
|
#include <errno.h>
|
2022-09-30 11:44:15 -04:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
2020-11-06 09:49:49 -05:00
|
|
|
|
2022-09-23 11:15:58 -04:00
|
|
|
#include <netinet/in.h>
|
2020-11-06 09:49:49 -05:00
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
2022-09-30 11:44:15 -04:00
|
|
|
#include <haproxy/api.h>
|
|
|
|
|
#include <haproxy/buf.h>
|
2020-11-06 09:49:49 -05:00
|
|
|
#include <haproxy/connection.h>
|
2022-11-16 05:01:02 -05:00
|
|
|
#include <haproxy/dynbuf.h>
|
2022-09-30 11:44:15 -04:00
|
|
|
#include <haproxy/fd.h>
|
|
|
|
|
#include <haproxy/global-t.h>
|
|
|
|
|
#include <haproxy/list.h>
|
2020-11-06 09:49:49 -05:00
|
|
|
#include <haproxy/listener.h>
|
2022-10-24 11:08:43 -04:00
|
|
|
#include <haproxy/log.h>
|
2022-09-30 11:44:15 -04:00
|
|
|
#include <haproxy/pool.h>
|
2022-05-17 11:23:16 -04:00
|
|
|
#include <haproxy/proto_quic.h>
|
2022-09-30 11:44:15 -04:00
|
|
|
#include <haproxy/proxy-t.h>
|
2023-11-23 13:21:03 -05:00
|
|
|
#include <haproxy/quic_cid.h>
|
2022-09-30 12:11:13 -04:00
|
|
|
#include <haproxy/quic_conn.h>
|
2023-07-25 09:42:16 -04:00
|
|
|
#include <haproxy/quic_rx.h>
|
2022-01-19 10:18:44 -05:00
|
|
|
#include <haproxy/quic_sock.h>
|
2022-09-30 11:44:15 -04:00
|
|
|
#include <haproxy/quic_tp-t.h>
|
2023-10-06 11:39:00 -04:00
|
|
|
#include <haproxy/quic_trace.h>
|
2021-10-07 10:44:05 -04:00
|
|
|
#include <haproxy/session.h>
|
2022-09-30 11:44:15 -04:00
|
|
|
#include <haproxy/stats-t.h>
|
|
|
|
|
#include <haproxy/task.h>
|
2022-09-27 08:22:09 -04:00
|
|
|
#include <haproxy/trace.h>
|
2022-03-24 11:06:26 -04:00
|
|
|
#include <haproxy/tools.h>
|
2022-10-24 11:40:37 -04:00
|
|
|
#include <haproxy/trace.h>
|
|
|
|
|
|
2023-11-21 13:54:16 -05:00
|
|
|
/* Log only first EACCES bind() error runtime occurrence. */
|
2023-10-03 10:11:40 -04:00
|
|
|
static volatile char quic_bind_eacces_warn = 0;
|
|
|
|
|
|
2022-04-11 10:20:00 -04:00
|
|
|
/* Retrieve a connection's source address. Returns -1 on failure. */
|
|
|
|
|
int quic_sock_get_src(struct connection *conn, struct sockaddr *addr, socklen_t len)
|
|
|
|
|
{
|
|
|
|
|
struct quic_conn *qc;
|
|
|
|
|
|
2022-04-11 08:18:10 -04:00
|
|
|
if (!conn || !conn->handle.qc)
|
2022-04-11 10:20:00 -04:00
|
|
|
return -1;
|
|
|
|
|
|
2022-04-11 08:18:10 -04:00
|
|
|
qc = conn->handle.qc;
|
2022-04-11 10:20:00 -04:00
|
|
|
if (conn_is_back(conn)) {
|
|
|
|
|
/* no source address defined for outgoing connections for now */
|
|
|
|
|
return -1;
|
|
|
|
|
} else {
|
|
|
|
|
/* front connection, return the peer's address */
|
|
|
|
|
if (len > sizeof(qc->peer_addr))
|
|
|
|
|
len = sizeof(qc->peer_addr);
|
|
|
|
|
memcpy(addr, &qc->peer_addr, len);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Retrieve a connection's destination address. Returns -1 on failure. */
|
|
|
|
|
int quic_sock_get_dst(struct connection *conn, struct sockaddr *addr, socklen_t len)
|
|
|
|
|
{
|
|
|
|
|
struct quic_conn *qc;
|
|
|
|
|
|
2022-04-11 08:18:10 -04:00
|
|
|
if (!conn || !conn->handle.qc)
|
2022-04-11 10:20:00 -04:00
|
|
|
return -1;
|
|
|
|
|
|
2022-04-11 08:18:10 -04:00
|
|
|
qc = conn->handle.qc;
|
2022-04-11 10:20:00 -04:00
|
|
|
if (conn_is_back(conn)) {
|
|
|
|
|
/* back connection, return the peer's address */
|
|
|
|
|
if (len > sizeof(qc->peer_addr))
|
|
|
|
|
len = sizeof(qc->peer_addr);
|
|
|
|
|
memcpy(addr, &qc->peer_addr, len);
|
|
|
|
|
} else {
|
2022-09-23 11:15:58 -04:00
|
|
|
struct sockaddr_storage *from;
|
|
|
|
|
|
|
|
|
|
/* Return listener address if IP_PKTINFO or friends are not
|
|
|
|
|
* supported by the socket.
|
2022-04-11 10:20:00 -04:00
|
|
|
*/
|
|
|
|
|
BUG_ON(!qc->li);
|
2022-09-23 11:15:58 -04:00
|
|
|
from = is_addr(&qc->local_addr) ? &qc->local_addr :
|
|
|
|
|
&qc->li->rx.addr;
|
|
|
|
|
if (len > sizeof(*from))
|
|
|
|
|
len = sizeof(*from);
|
|
|
|
|
memcpy(addr, from, len);
|
2022-04-11 10:20:00 -04:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-23 09:46:36 -05:00
|
|
|
/*
|
|
|
|
|
* Inspired from session_accept_fd().
|
|
|
|
|
* Instantiate a new connection (connection struct) to be attached to <qc>
|
|
|
|
|
* QUIC connection of <l> listener.
|
|
|
|
|
* Returns 1 if succeeded, 0 if not.
|
|
|
|
|
*/
|
|
|
|
|
static int new_quic_cli_conn(struct quic_conn *qc, struct listener *l,
|
|
|
|
|
struct sockaddr_storage *saddr)
|
|
|
|
|
{
|
|
|
|
|
struct connection *cli_conn;
|
|
|
|
|
|
|
|
|
|
if (unlikely((cli_conn = conn_new(&l->obj_type)) == NULL))
|
|
|
|
|
goto out;
|
|
|
|
|
|
2022-04-08 08:34:31 -04:00
|
|
|
if (!sockaddr_alloc(&cli_conn->src, saddr, sizeof *saddr))
|
2020-11-23 09:46:36 -05:00
|
|
|
goto out_free_conn;
|
|
|
|
|
|
2022-05-02 11:47:46 -04:00
|
|
|
cli_conn->flags |= CO_FL_FDLESS;
|
2020-11-23 09:46:36 -05:00
|
|
|
qc->conn = cli_conn;
|
2022-04-11 08:18:10 -04:00
|
|
|
cli_conn->handle.qc = qc;
|
2020-11-23 09:46:36 -05:00
|
|
|
|
|
|
|
|
cli_conn->target = &l->obj_type;
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
out_free_conn:
|
2021-06-14 04:31:43 -04:00
|
|
|
qc->conn = NULL;
|
2020-11-23 09:46:36 -05:00
|
|
|
conn_stop_tracking(cli_conn);
|
|
|
|
|
conn_xprt_close(cli_conn);
|
|
|
|
|
conn_free(cli_conn);
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2020-11-06 09:49:49 -05:00
|
|
|
|
|
|
|
|
/* Tests if the receiver supports accepting connections. Returns positive on
|
|
|
|
|
* success, 0 if not possible
|
|
|
|
|
*/
|
|
|
|
|
int quic_sock_accepting_conn(const struct receiver *rx)
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Accept an incoming connection from listener <l>, and return it, as well as
|
|
|
|
|
* a CO_AC_* status code into <status> if not null. Null is returned on error.
|
|
|
|
|
* <l> must be a valid listener with a valid frontend.
|
|
|
|
|
*/
|
|
|
|
|
struct connection *quic_sock_accept_conn(struct listener *l, int *status)
|
|
|
|
|
{
|
2020-11-23 09:46:36 -05:00
|
|
|
struct quic_conn *qc;
|
2023-04-21 04:46:45 -04:00
|
|
|
struct li_per_thread *lthr = &l->per_thr[ti->ltid];
|
2020-11-23 09:46:36 -05:00
|
|
|
|
2022-01-19 10:01:05 -05:00
|
|
|
qc = MT_LIST_POP(<hr->quic_accept.conns, struct quic_conn *, accept_list);
|
2023-04-17 03:31:16 -04:00
|
|
|
if (!qc || qc->flags & (QUIC_FL_CONN_CLOSING|QUIC_FL_CONN_DRAINING))
|
2022-01-19 10:01:05 -05:00
|
|
|
goto done;
|
2020-11-23 09:46:36 -05:00
|
|
|
|
2022-01-19 10:01:05 -05:00
|
|
|
if (!new_quic_cli_conn(qc, l, &qc->peer_addr))
|
2020-11-23 09:46:36 -05:00
|
|
|
goto err;
|
|
|
|
|
|
|
|
|
|
done:
|
2022-01-19 10:01:05 -05:00
|
|
|
*status = CO_AC_DONE;
|
2023-11-08 08:29:31 -05:00
|
|
|
|
|
|
|
|
if (qc) {
|
|
|
|
|
BUG_ON(l->rx.quic_curr_accept <= 0);
|
|
|
|
|
HA_ATOMIC_DEC(&l->rx.quic_curr_accept);
|
|
|
|
|
return qc->conn;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2020-11-23 09:46:36 -05:00
|
|
|
|
|
|
|
|
err:
|
2022-01-19 10:01:05 -05:00
|
|
|
/* in case of error reinsert the element to process it later. */
|
|
|
|
|
MT_LIST_INSERT(<hr->quic_accept.conns, &qc->accept_list);
|
|
|
|
|
|
|
|
|
|
*status = CO_AC_PAUSE;
|
|
|
|
|
return NULL;
|
2020-11-06 09:49:49 -05:00
|
|
|
}
|
|
|
|
|
|
2022-09-27 08:22:09 -04:00
|
|
|
/* QUIC datagrams handler task. */
|
|
|
|
|
struct task *quic_lstnr_dghdlr(struct task *t, void *ctx, unsigned int state)
|
|
|
|
|
{
|
|
|
|
|
struct quic_dghdlr *dghdlr = ctx;
|
|
|
|
|
struct quic_dgram *dgram;
|
|
|
|
|
int max_dgrams = global.tune.maxpollevents;
|
|
|
|
|
|
|
|
|
|
TRACE_ENTER(QUIC_EV_CONN_LPKT);
|
|
|
|
|
|
|
|
|
|
while ((dgram = MT_LIST_POP(&dghdlr->dgrams, typeof(dgram), handler_list))) {
|
|
|
|
|
if (quic_dgram_parse(dgram, NULL, dgram->owner)) {
|
|
|
|
|
/* TODO should we requeue the datagram ? */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (--max_dgrams <= 0)
|
|
|
|
|
goto stop_here;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TRACE_LEAVE(QUIC_EV_CONN_LPKT);
|
|
|
|
|
return t;
|
|
|
|
|
|
|
|
|
|
stop_here:
|
|
|
|
|
/* too much work done at once, come back here later */
|
|
|
|
|
if (!MT_LIST_ISEMPTY(&dghdlr->dgrams))
|
|
|
|
|
tasklet_wakeup((struct tasklet *)t);
|
|
|
|
|
|
|
|
|
|
TRACE_LEAVE(QUIC_EV_CONN_LPKT);
|
|
|
|
|
return t;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-27 06:01:36 -05:00
|
|
|
/* Retrieve the DCID from a QUIC datagram or packet at <pos> position,
|
|
|
|
|
* <end> being at one byte past the end of this datagram.
|
|
|
|
|
* Returns 1 if succeeded, 0 if not.
|
|
|
|
|
*/
|
|
|
|
|
static int quic_get_dgram_dcid(unsigned char *pos, const unsigned char *end,
|
|
|
|
|
unsigned char **dcid, size_t *dcid_len)
|
|
|
|
|
{
|
|
|
|
|
int ret = 0, long_header;
|
|
|
|
|
size_t minlen, skip;
|
|
|
|
|
|
|
|
|
|
TRACE_ENTER(QUIC_EV_CONN_RXPKT);
|
|
|
|
|
|
|
|
|
|
if (!(*pos & QUIC_PACKET_FIXED_BIT)) {
|
|
|
|
|
TRACE_PROTO("fixed bit not set", QUIC_EV_CONN_RXPKT);
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
long_header = *pos & QUIC_PACKET_LONG_HEADER_BIT;
|
|
|
|
|
minlen = long_header ? QUIC_LONG_PACKET_MINLEN :
|
|
|
|
|
QUIC_SHORT_PACKET_MINLEN + QUIC_HAP_CID_LEN + QUIC_TLS_TAG_LEN;
|
|
|
|
|
skip = long_header ? QUIC_LONG_PACKET_DCID_OFF : QUIC_SHORT_PACKET_DCID_OFF;
|
|
|
|
|
if (end - pos < minlen)
|
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
|
|
pos += skip;
|
|
|
|
|
*dcid_len = long_header ? *pos++ : QUIC_HAP_CID_LEN;
|
|
|
|
|
if (*dcid_len > QUIC_CID_MAXLEN || end - pos <= *dcid_len)
|
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
|
|
*dcid = pos;
|
|
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
|
leave:
|
|
|
|
|
TRACE_LEAVE(QUIC_EV_CONN_RXPKT);
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
TRACE_PROTO("wrong datagram", QUIC_EV_CONN_RXPKT);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-04-24 09:49:36 -04:00
|
|
|
/* Retrieve the DCID from the datagram found at <pos> position and deliver it to the
|
2022-05-17 11:23:16 -04:00
|
|
|
* correct datagram handler.
|
|
|
|
|
* Return 1 if a correct datagram could be found, 0 if not.
|
|
|
|
|
*/
|
2023-04-24 09:49:36 -04:00
|
|
|
static int quic_lstnr_dgram_dispatch(unsigned char *pos, size_t len, void *owner,
|
2022-05-17 11:23:16 -04:00
|
|
|
struct sockaddr_storage *saddr,
|
2022-09-23 11:15:58 -04:00
|
|
|
struct sockaddr_storage *daddr,
|
2022-05-17 11:23:16 -04:00
|
|
|
struct quic_dgram *new_dgram, struct list *dgrams)
|
|
|
|
|
{
|
|
|
|
|
struct quic_dgram *dgram;
|
|
|
|
|
unsigned char *dcid;
|
|
|
|
|
size_t dcid_len;
|
|
|
|
|
int cid_tid;
|
|
|
|
|
|
2023-04-24 09:49:36 -04:00
|
|
|
if (!len || !quic_get_dgram_dcid(pos, pos + len, &dcid, &dcid_len))
|
2022-05-17 11:23:16 -04:00
|
|
|
goto err;
|
|
|
|
|
|
|
|
|
|
dgram = new_dgram ? new_dgram : pool_alloc(pool_head_quic_dgram);
|
|
|
|
|
if (!dgram)
|
|
|
|
|
goto err;
|
|
|
|
|
|
2023-04-24 09:49:36 -04:00
|
|
|
if ((cid_tid = quic_get_cid_tid(dcid, dcid_len, saddr, pos, len)) < 0) {
|
2023-04-13 11:42:34 -04:00
|
|
|
/* Use the current thread if CID not found. If a clients opens
|
|
|
|
|
* a connection with multiple packets, it is possible that
|
|
|
|
|
* several threads will deal with datagrams sharing the same
|
|
|
|
|
* CID. For this reason, the CID tree insertion will be
|
|
|
|
|
* conducted as an atomic operation and the datagram ultimately
|
|
|
|
|
* redispatch by the late thread.
|
2023-04-18 05:10:54 -04:00
|
|
|
*/
|
2023-04-13 11:42:34 -04:00
|
|
|
cid_tid = tid;
|
2023-04-18 05:10:54 -04:00
|
|
|
}
|
2022-05-17 11:23:16 -04:00
|
|
|
|
|
|
|
|
/* All the members must be initialized! */
|
|
|
|
|
dgram->owner = owner;
|
2023-04-24 09:49:36 -04:00
|
|
|
dgram->buf = pos;
|
2022-05-17 11:23:16 -04:00
|
|
|
dgram->len = len;
|
|
|
|
|
dgram->dcid = dcid;
|
|
|
|
|
dgram->dcid_len = dcid_len;
|
|
|
|
|
dgram->saddr = *saddr;
|
2022-09-23 11:15:58 -04:00
|
|
|
dgram->daddr = *daddr;
|
2022-05-17 11:23:16 -04:00
|
|
|
dgram->qc = NULL;
|
2022-10-06 09:16:22 -04:00
|
|
|
|
|
|
|
|
/* Attached datagram to its quic_receiver_buf and quic_dghdlrs. */
|
|
|
|
|
LIST_APPEND(dgrams, &dgram->recv_list);
|
|
|
|
|
MT_LIST_APPEND(&quic_dghdlrs[cid_tid].dgrams, &dgram->handler_list);
|
2022-05-17 11:23:16 -04:00
|
|
|
|
2022-08-05 02:45:56 -04:00
|
|
|
/* typically quic_lstnr_dghdlr() */
|
2022-05-17 11:23:16 -04:00
|
|
|
tasklet_wakeup(quic_dghdlrs[cid_tid].task);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
err:
|
2022-08-12 05:55:20 -04:00
|
|
|
pool_free(pool_head_quic_dgram, new_dgram);
|
2022-05-17 11:23:16 -04:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-06 08:45:09 -04:00
|
|
|
/* This function is responsible to remove unused datagram attached in front of
|
|
|
|
|
* <buf>. Each instances will be freed until a not yet consumed datagram is
|
|
|
|
|
* found or end of the list is hit. The last unused datagram found is not freed
|
|
|
|
|
* and is instead returned so that the caller can reuse it if needed.
|
|
|
|
|
*
|
2022-10-29 00:34:32 -04:00
|
|
|
* Returns the last unused datagram or NULL if no occurrence found.
|
2022-10-06 08:45:09 -04:00
|
|
|
*/
|
2023-04-24 09:49:36 -04:00
|
|
|
static struct quic_dgram *quic_rxbuf_purge_dgrams(struct quic_receiver_buf *rbuf)
|
2022-10-06 08:45:09 -04:00
|
|
|
{
|
|
|
|
|
struct quic_dgram *cur, *prev = NULL;
|
|
|
|
|
|
2023-04-24 09:49:36 -04:00
|
|
|
while (!LIST_ISEMPTY(&rbuf->dgram_list)) {
|
|
|
|
|
cur = LIST_ELEM(rbuf->dgram_list.n, struct quic_dgram *, recv_list);
|
2022-10-06 08:45:09 -04:00
|
|
|
|
|
|
|
|
/* Loop until a not yet consumed datagram is found. */
|
2022-10-25 05:38:21 -04:00
|
|
|
if (HA_ATOMIC_LOAD(&cur->buf))
|
2022-10-06 08:45:09 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Clear buffer of current unused datagram. */
|
|
|
|
|
LIST_DELETE(&cur->recv_list);
|
2023-04-24 09:49:36 -04:00
|
|
|
b_del(&rbuf->buf, cur->len);
|
2022-10-06 08:45:09 -04:00
|
|
|
|
|
|
|
|
/* Free last found unused datagram. */
|
2023-04-22 11:47:33 -04:00
|
|
|
pool_free(pool_head_quic_dgram, prev);
|
2022-10-06 08:45:09 -04:00
|
|
|
prev = cur;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return last unused datagram found. */
|
|
|
|
|
return prev;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-23 11:15:58 -04:00
|
|
|
/* Receive data from datagram socket <fd>. Data are placed in <out> buffer of
|
|
|
|
|
* length <len>.
|
|
|
|
|
*
|
|
|
|
|
* Datagram addresses will be returned via the next arguments. <from> will be
|
|
|
|
|
* the peer address and <to> the reception one. Note that <to> can only be
|
|
|
|
|
* retrieved if the socket supports IP_PKTINFO or affiliated options. If not,
|
|
|
|
|
* <to> will be set as AF_UNSPEC. The caller must specify <to_port> to ensure
|
|
|
|
|
* that <to> address is completely filled.
|
|
|
|
|
*
|
|
|
|
|
* Returns value from recvmsg syscall.
|
|
|
|
|
*/
|
|
|
|
|
static ssize_t quic_recv(int fd, void *out, size_t len,
|
|
|
|
|
struct sockaddr *from, socklen_t from_len,
|
|
|
|
|
struct sockaddr *to, socklen_t to_len,
|
|
|
|
|
uint16_t dst_port)
|
|
|
|
|
{
|
|
|
|
|
union pktinfo {
|
|
|
|
|
#ifdef IP_PKTINFO
|
|
|
|
|
struct in_pktinfo in;
|
|
|
|
|
#else /* !IP_PKTINFO */
|
|
|
|
|
struct in_addr addr;
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef IPV6_RECVPKTINFO
|
|
|
|
|
struct in6_pktinfo in6;
|
|
|
|
|
#endif
|
|
|
|
|
};
|
|
|
|
|
char cdata[CMSG_SPACE(sizeof(union pktinfo))];
|
|
|
|
|
struct msghdr msg;
|
|
|
|
|
struct iovec vec;
|
|
|
|
|
struct cmsghdr *cmsg;
|
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
|
|
vec.iov_base = out;
|
|
|
|
|
vec.iov_len = len;
|
|
|
|
|
|
|
|
|
|
memset(&msg, 0, sizeof(msg));
|
|
|
|
|
msg.msg_name = from;
|
|
|
|
|
msg.msg_namelen = from_len;
|
|
|
|
|
msg.msg_iov = &vec;
|
|
|
|
|
msg.msg_iovlen = 1;
|
|
|
|
|
msg.msg_control = &cdata;
|
|
|
|
|
msg.msg_controllen = sizeof(cdata);
|
|
|
|
|
|
|
|
|
|
clear_addr((struct sockaddr_storage *)to);
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
ret = recvmsg(fd, &msg, 0);
|
|
|
|
|
} while (ret < 0 && errno == EINTR);
|
|
|
|
|
|
|
|
|
|
/* TODO handle errno. On EAGAIN/EWOULDBLOCK use fd_cant_recv() if
|
|
|
|
|
* using dedicated connection socket.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
|
|
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
|
|
|
|
switch (cmsg->cmsg_level) {
|
|
|
|
|
case IPPROTO_IP:
|
|
|
|
|
#if defined(IP_PKTINFO)
|
|
|
|
|
if (cmsg->cmsg_type == IP_PKTINFO) {
|
|
|
|
|
struct sockaddr_in *in = (struct sockaddr_in *)to;
|
|
|
|
|
struct in_pktinfo *info = (struct in_pktinfo *)CMSG_DATA(cmsg);
|
|
|
|
|
|
|
|
|
|
if (to_len >= sizeof(struct sockaddr_in)) {
|
|
|
|
|
in->sin_family = AF_INET;
|
|
|
|
|
in->sin_addr = info->ipi_addr;
|
|
|
|
|
in->sin_port = dst_port;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#elif defined(IP_RECVDSTADDR)
|
|
|
|
|
if (cmsg->cmsg_type == IP_RECVDSTADDR) {
|
|
|
|
|
struct sockaddr_in *in = (struct sockaddr_in *)to;
|
|
|
|
|
struct in_addr *info = (struct in_addr *)CMSG_DATA(cmsg);
|
|
|
|
|
|
|
|
|
|
if (to_len >= sizeof(struct sockaddr_in)) {
|
|
|
|
|
in->sin_family = AF_INET;
|
|
|
|
|
in->sin_addr.s_addr = info->s_addr;
|
|
|
|
|
in->sin_port = dst_port;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif /* IP_PKTINFO || IP_RECVDSTADDR */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IPPROTO_IPV6:
|
|
|
|
|
#ifdef IPV6_RECVPKTINFO
|
|
|
|
|
if (cmsg->cmsg_type == IPV6_PKTINFO) {
|
|
|
|
|
struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)to;
|
|
|
|
|
struct in6_pktinfo *info6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
|
|
|
|
|
|
|
|
|
|
if (to_len >= sizeof(struct sockaddr_in6)) {
|
|
|
|
|
in6->sin6_family = AF_INET6;
|
|
|
|
|
memcpy(&in6->sin6_addr, &info6->ipi6_addr, sizeof(in6->sin6_addr));
|
|
|
|
|
in6->sin6_port = dst_port;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
end:
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-06 09:49:49 -05:00
|
|
|
/* Function called on a read event from a listening socket. It tries
|
|
|
|
|
* to handle as many connections as possible.
|
|
|
|
|
*/
|
2022-10-24 11:40:37 -04:00
|
|
|
void quic_lstnr_sock_fd_iocb(int fd)
|
2020-11-06 09:49:49 -05:00
|
|
|
{
|
|
|
|
|
ssize_t ret;
|
2022-10-06 09:16:22 -04:00
|
|
|
struct quic_receiver_buf *rxbuf;
|
2020-11-06 09:49:49 -05:00
|
|
|
struct buffer *buf;
|
|
|
|
|
struct listener *l = objt_listener(fdtab[fd].owner);
|
2021-11-08 05:23:17 -05:00
|
|
|
struct quic_transport_params *params;
|
2020-11-06 09:49:49 -05:00
|
|
|
/* Source address */
|
2022-09-23 11:15:58 -04:00
|
|
|
struct sockaddr_storage saddr = {0}, daddr = {0};
|
2022-01-27 06:19:28 -05:00
|
|
|
size_t max_sz, cspace;
|
2022-06-23 15:05:05 -04:00
|
|
|
struct quic_dgram *new_dgram;
|
2022-02-02 03:44:22 -05:00
|
|
|
unsigned char *dgram_buf;
|
2022-06-30 05:28:56 -04:00
|
|
|
int max_dgrams;
|
2020-11-06 09:49:49 -05:00
|
|
|
|
2021-09-15 07:58:49 -04:00
|
|
|
BUG_ON(!l);
|
2020-11-06 09:49:49 -05:00
|
|
|
|
2022-06-23 12:00:37 -04:00
|
|
|
new_dgram = NULL;
|
2021-11-08 05:23:17 -05:00
|
|
|
if (!l)
|
|
|
|
|
return;
|
|
|
|
|
|
2021-04-06 11:23:40 -04:00
|
|
|
if (!(fdtab[fd].state & FD_POLL_IN) || !fd_recv_ready(fd))
|
2020-11-06 09:49:49 -05:00
|
|
|
return;
|
|
|
|
|
|
2022-10-06 09:16:22 -04:00
|
|
|
rxbuf = MT_LIST_POP(&l->rx.rxbuf_list, typeof(rxbuf), rxbuf_el);
|
2021-11-19 09:49:29 -05:00
|
|
|
if (!rxbuf)
|
2021-11-08 05:23:17 -05:00
|
|
|
goto out;
|
2022-01-27 05:31:50 -05:00
|
|
|
|
2021-11-19 09:49:29 -05:00
|
|
|
buf = &rxbuf->buf;
|
2021-11-08 05:23:17 -05:00
|
|
|
|
2022-06-30 05:28:56 -04:00
|
|
|
max_dgrams = global.tune.maxpollevents;
|
|
|
|
|
start:
|
2022-07-29 13:26:53 -04:00
|
|
|
/* Try to reuse an existing dgram. Note that there is always at
|
2022-06-23 15:05:05 -04:00
|
|
|
* least one datagram to pick, except the first time we enter
|
|
|
|
|
* this function for this <rxbuf> buffer.
|
|
|
|
|
*/
|
2022-10-06 08:45:09 -04:00
|
|
|
new_dgram = quic_rxbuf_purge_dgrams(rxbuf);
|
2022-01-27 05:31:50 -05:00
|
|
|
|
2021-11-08 05:23:17 -05:00
|
|
|
params = &l->bind_conf->quic_params;
|
2021-11-02 05:14:44 -04:00
|
|
|
max_sz = params->max_udp_payload_size;
|
2022-01-27 06:19:28 -05:00
|
|
|
cspace = b_contig_space(buf);
|
|
|
|
|
if (cspace < max_sz) {
|
2022-10-27 11:56:27 -04:00
|
|
|
struct proxy *px = l->bind_conf->frontend;
|
|
|
|
|
struct quic_counters *prx_counters = EXTRA_COUNTERS_GET(px->extra_counters_fe, &quic_stats_module);
|
2022-01-28 07:10:24 -05:00
|
|
|
struct quic_dgram *dgram;
|
|
|
|
|
|
2022-06-23 11:47:10 -04:00
|
|
|
/* Do no mark <buf> as full, and do not try to consume it
|
2022-08-08 15:10:58 -04:00
|
|
|
* if the contiguous remaining space is not at the end
|
2022-06-23 11:47:10 -04:00
|
|
|
*/
|
2022-10-27 11:56:27 -04:00
|
|
|
if (b_tail(buf) + cspace < b_wrap(buf)) {
|
|
|
|
|
HA_ATOMIC_INC(&prx_counters->rxbuf_full);
|
2022-06-23 11:47:10 -04:00
|
|
|
goto out;
|
2022-10-27 11:56:27 -04:00
|
|
|
}
|
2022-06-23 11:47:10 -04:00
|
|
|
|
2022-01-28 07:10:24 -05:00
|
|
|
/* Allocate a fake datagram, without data to locate
|
|
|
|
|
* the end of the RX buffer (required during purging).
|
|
|
|
|
*/
|
2022-08-08 15:10:58 -04:00
|
|
|
dgram = pool_alloc(pool_head_quic_dgram);
|
2022-01-28 07:10:24 -05:00
|
|
|
if (!dgram)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2022-08-08 15:10:58 -04:00
|
|
|
/* Initialize only the useful members of this fake datagram. */
|
|
|
|
|
dgram->buf = NULL;
|
2022-01-28 07:10:24 -05:00
|
|
|
dgram->len = cspace;
|
2022-08-08 15:10:58 -04:00
|
|
|
/* Append this datagram only to the RX buffer list. It will
|
|
|
|
|
* not be treated by any datagram handler.
|
|
|
|
|
*/
|
2022-10-06 09:16:22 -04:00
|
|
|
LIST_APPEND(&rxbuf->dgram_list, &dgram->recv_list);
|
2022-06-23 11:47:10 -04:00
|
|
|
|
2022-01-27 06:19:28 -05:00
|
|
|
/* Consume the remaining space */
|
|
|
|
|
b_add(buf, cspace);
|
2022-10-27 11:56:27 -04:00
|
|
|
if (b_contig_space(buf) < max_sz) {
|
|
|
|
|
HA_ATOMIC_INC(&prx_counters->rxbuf_full);
|
2021-11-02 05:14:44 -04:00
|
|
|
goto out;
|
2022-10-27 11:56:27 -04:00
|
|
|
}
|
2021-11-02 05:14:44 -04:00
|
|
|
}
|
|
|
|
|
|
2022-02-02 03:44:22 -05:00
|
|
|
dgram_buf = (unsigned char *)b_tail(buf);
|
2022-09-23 11:15:58 -04:00
|
|
|
ret = quic_recv(fd, dgram_buf, max_sz,
|
|
|
|
|
(struct sockaddr *)&saddr, sizeof(saddr),
|
|
|
|
|
(struct sockaddr *)&daddr, sizeof(daddr),
|
|
|
|
|
get_net_port(&l->rx.addr));
|
|
|
|
|
if (ret <= 0)
|
|
|
|
|
goto out;
|
2020-11-06 09:49:49 -05:00
|
|
|
|
2021-11-02 05:14:44 -04:00
|
|
|
b_add(buf, ret);
|
2022-09-23 11:15:58 -04:00
|
|
|
if (!quic_lstnr_dgram_dispatch(dgram_buf, ret, l, &saddr, &daddr,
|
2022-10-06 09:16:22 -04:00
|
|
|
new_dgram, &rxbuf->dgram_list)) {
|
2022-01-27 05:31:50 -05:00
|
|
|
/* If wrong, consume this datagram */
|
2022-11-24 09:24:38 -05:00
|
|
|
b_sub(buf, ret);
|
2022-01-27 05:31:50 -05:00
|
|
|
}
|
2022-06-23 12:00:37 -04:00
|
|
|
new_dgram = NULL;
|
2022-06-30 05:28:56 -04:00
|
|
|
if (--max_dgrams > 0)
|
|
|
|
|
goto start;
|
2021-11-02 05:14:44 -04:00
|
|
|
out:
|
2022-06-23 12:00:37 -04:00
|
|
|
pool_free(pool_head_quic_dgram, new_dgram);
|
2022-10-06 09:16:22 -04:00
|
|
|
MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->rxbuf_el);
|
2020-11-06 09:49:49 -05:00
|
|
|
}
|
2022-01-19 09:46:11 -05:00
|
|
|
|
2022-10-24 11:40:37 -04:00
|
|
|
/* FD-owned quic-conn socket callback. */
|
2023-03-10 06:04:02 -05:00
|
|
|
void quic_conn_sock_fd_iocb(int fd)
|
2022-10-24 11:40:37 -04:00
|
|
|
{
|
2022-11-16 05:01:02 -05:00
|
|
|
struct quic_conn *qc = fdtab[fd].owner;
|
2022-10-24 11:40:37 -04:00
|
|
|
|
|
|
|
|
TRACE_ENTER(QUIC_EV_CONN_RCV, qc);
|
|
|
|
|
|
2023-02-28 09:11:09 -05:00
|
|
|
if (fd_send_active(fd) && fd_send_ready(fd)) {
|
|
|
|
|
TRACE_DEVEL("send ready", QUIC_EV_CONN_RCV, qc);
|
|
|
|
|
fd_stop_send(fd);
|
|
|
|
|
tasklet_wakeup_after(NULL, qc->wait_event.tasklet);
|
2023-02-28 09:11:26 -05:00
|
|
|
qc_notify_send(qc);
|
2023-02-28 09:11:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fd_recv_ready(fd)) {
|
2023-08-11 02:57:47 -04:00
|
|
|
TRACE_DEVEL("recv ready", QUIC_EV_CONN_RCV, qc);
|
2023-02-28 09:11:09 -05:00
|
|
|
tasklet_wakeup_after(NULL, qc->wait_event.tasklet);
|
|
|
|
|
fd_stop_recv(fd);
|
|
|
|
|
}
|
2022-10-24 11:40:37 -04:00
|
|
|
|
|
|
|
|
TRACE_LEAVE(QUIC_EV_CONN_RCV, qc);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-03 14:52:20 -04:00
|
|
|
/* Send a datagram stored into <buf> buffer with <sz> as size.
|
|
|
|
|
* The caller must ensure there is at least <sz> bytes in this buffer.
|
2022-08-05 05:56:36 -04:00
|
|
|
*
|
MEDIUM: quic: improve fatal error handling on send
Send is conducted through qc_send_ppkts() for a QUIC connection. There
is two types of error which can be encountered on sendto() or affiliated
syscalls :
* transient error. In this case, sending is simulated with the remaining
data and retransmission process is used to have the opportunity to
retry emission
* fatal error. If this happens, the connection should be closed as soon
as possible. This is done via qc_kill_conn() function. Until this
patch, only ECONNREFUSED errno was considered as fatal.
Modify the QUIC send API to be able to differentiate transient and fatal
errors more easily. This is done by fixing the return value of the
sendto() wrapper qc_snd_buf() :
* on fatal error, a negative error code is returned. This is now the
case for every errno except EAGAIN, EWOULDBLOCK, ENOTCONN, EINPROGRESS
and EBADF.
* on a transient error, 0 is returned. This is the case for the listed
errno values above and also if a partial send has been conducted by
the kernel.
* on success, the return value of sendto() syscall is returned.
This commit will be useful to be able to handle transient error with a
quic-conn owned socket. In this case, the socket should be subscribed to
the poller and no simulated send will be conducted.
This commit allows errno management to be confined in the quic-sock
module which is a nice cleanup.
On a final note, EBADF should be considered as fatal. This will be the
subject of a next commit.
This should be backported up to 2.7.
2023-02-23 05:18:38 -05:00
|
|
|
* Returns the total bytes sent over the socket. 0 is returned if a transient
|
|
|
|
|
* error is encountered which allows send to be retry later. A negative value
|
|
|
|
|
* is used for a fatal error which guarantee that all future send operation for
|
|
|
|
|
* this connection will fail.
|
2022-08-05 05:56:36 -04:00
|
|
|
*
|
2022-08-03 14:52:20 -04:00
|
|
|
* TODO standardize this function for a generic UDP sendto wrapper. This can be
|
2022-02-09 09:43:07 -05:00
|
|
|
* done by removing the <qc> arg and replace it with address/port.
|
|
|
|
|
*/
|
2022-08-05 05:56:36 -04:00
|
|
|
int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz,
|
MEDIUM: quic: improve fatal error handling on send
Send is conducted through qc_send_ppkts() for a QUIC connection. There
is two types of error which can be encountered on sendto() or affiliated
syscalls :
* transient error. In this case, sending is simulated with the remaining
data and retransmission process is used to have the opportunity to
retry emission
* fatal error. If this happens, the connection should be closed as soon
as possible. This is done via qc_kill_conn() function. Until this
patch, only ECONNREFUSED errno was considered as fatal.
Modify the QUIC send API to be able to differentiate transient and fatal
errors more easily. This is done by fixing the return value of the
sendto() wrapper qc_snd_buf() :
* on fatal error, a negative error code is returned. This is now the
case for every errno except EAGAIN, EWOULDBLOCK, ENOTCONN, EINPROGRESS
and EBADF.
* on a transient error, 0 is returned. This is the case for the listed
errno values above and also if a partial send has been conducted by
the kernel.
* on success, the return value of sendto() syscall is returned.
This commit will be useful to be able to handle transient error with a
quic-conn owned socket. In this case, the socket should be subscribed to
the poller and no simulated send will be conducted.
This commit allows errno management to be confined in the quic-sock
module which is a nice cleanup.
On a final note, EBADF should be considered as fatal. This will be the
subject of a next commit.
This should be backported up to 2.7.
2023-02-23 05:18:38 -05:00
|
|
|
int flags)
|
2022-02-09 09:43:07 -05:00
|
|
|
{
|
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
2022-08-03 14:52:20 -04:00
|
|
|
do {
|
2022-11-21 08:48:57 -05:00
|
|
|
if (qc_test_fd(qc)) {
|
2023-02-28 09:11:09 -05:00
|
|
|
if (!fd_send_ready(qc->fd))
|
|
|
|
|
return 0;
|
|
|
|
|
|
2022-11-21 08:48:57 -05:00
|
|
|
ret = send(qc->fd, b_peek(buf, b_head_ofs(buf)), sz,
|
|
|
|
|
MSG_DONTWAIT | MSG_NOSIGNAL);
|
|
|
|
|
}
|
2023-01-19 12:05:54 -05:00
|
|
|
#if defined(IP_PKTINFO) || defined(IP_RECVDSTADDR) || defined(IPV6_RECVPKTINFO)
|
|
|
|
|
else if (is_addr(&qc->local_addr)) {
|
|
|
|
|
struct msghdr msg = { 0 };
|
|
|
|
|
struct iovec vec;
|
|
|
|
|
struct cmsghdr *cmsg;
|
|
|
|
|
#ifdef IP_PKTINFO
|
|
|
|
|
struct in_pktinfo in;
|
|
|
|
|
#endif /* IP_PKTINFO */
|
|
|
|
|
#ifdef IPV6_RECVPKTINFO
|
|
|
|
|
struct in6_pktinfo in6;
|
|
|
|
|
#endif /* IPV6_RECVPKTINFO */
|
|
|
|
|
union {
|
|
|
|
|
#ifdef IP_PKTINFO
|
|
|
|
|
char buf[CMSG_SPACE(sizeof(in))];
|
|
|
|
|
#endif /* IP_PKTINFO */
|
|
|
|
|
#ifdef IPV6_RECVPKTINFO
|
|
|
|
|
char buf6[CMSG_SPACE(sizeof(in6))];
|
|
|
|
|
#endif /* IPV6_RECVPKTINFO */
|
|
|
|
|
char bufaddr[CMSG_SPACE(sizeof(struct in_addr))];
|
|
|
|
|
struct cmsghdr align;
|
|
|
|
|
} u;
|
|
|
|
|
|
|
|
|
|
vec.iov_base = b_peek(buf, b_head_ofs(buf));
|
|
|
|
|
vec.iov_len = sz;
|
|
|
|
|
msg.msg_name = &qc->peer_addr;
|
|
|
|
|
msg.msg_namelen = get_addr_len(&qc->peer_addr);
|
|
|
|
|
msg.msg_iov = &vec;
|
|
|
|
|
msg.msg_iovlen = 1;
|
|
|
|
|
|
|
|
|
|
switch (qc->local_addr.ss_family) {
|
|
|
|
|
case AF_INET:
|
|
|
|
|
#if defined(IP_PKTINFO)
|
|
|
|
|
memset(&in, 0, sizeof(in));
|
|
|
|
|
memcpy(&in.ipi_spec_dst,
|
|
|
|
|
&((struct sockaddr_in *)&qc->local_addr)->sin_addr,
|
|
|
|
|
sizeof(struct in_addr));
|
|
|
|
|
|
|
|
|
|
msg.msg_control = u.buf;
|
|
|
|
|
msg.msg_controllen = sizeof(u.buf);
|
|
|
|
|
|
|
|
|
|
cmsg = CMSG_FIRSTHDR(&msg);
|
|
|
|
|
cmsg->cmsg_level = IPPROTO_IP;
|
|
|
|
|
cmsg->cmsg_type = IP_PKTINFO;
|
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
|
|
|
|
|
memcpy(CMSG_DATA(cmsg), &in, sizeof(in));
|
|
|
|
|
#elif defined(IP_RECVDSTADDR)
|
|
|
|
|
msg.msg_control = u.bufaddr;
|
|
|
|
|
msg.msg_controllen = sizeof(u.bufaddr);
|
|
|
|
|
|
|
|
|
|
cmsg = CMSG_FIRSTHDR(&msg);
|
|
|
|
|
cmsg->cmsg_level = IPPROTO_IP;
|
|
|
|
|
cmsg->cmsg_type = IP_SENDSRCADDR;
|
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
|
|
|
|
|
memcpy(CMSG_DATA(cmsg),
|
|
|
|
|
&((struct sockaddr_in *)&qc->local_addr)->sin_addr,
|
|
|
|
|
sizeof(struct in_addr));
|
|
|
|
|
#endif /* IP_PKTINFO || IP_RECVDSTADDR */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case AF_INET6:
|
|
|
|
|
#ifdef IPV6_RECVPKTINFO
|
|
|
|
|
memset(&in6, 0, sizeof(in6));
|
|
|
|
|
memcpy(&in6.ipi6_addr,
|
|
|
|
|
&((struct sockaddr_in6 *)&qc->local_addr)->sin6_addr,
|
|
|
|
|
sizeof(struct in6_addr));
|
|
|
|
|
|
|
|
|
|
msg.msg_control = u.buf6;
|
|
|
|
|
msg.msg_controllen = sizeof(u.buf6);
|
|
|
|
|
|
|
|
|
|
cmsg = CMSG_FIRSTHDR(&msg);
|
|
|
|
|
cmsg->cmsg_level = IPPROTO_IPV6;
|
|
|
|
|
cmsg->cmsg_type = IPV6_PKTINFO;
|
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
|
|
|
|
|
memcpy(CMSG_DATA(cmsg), &in6, sizeof(in6));
|
|
|
|
|
#endif /* IPV6_RECVPKTINFO */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = sendmsg(qc->li->rx.fd, &msg,
|
|
|
|
|
MSG_DONTWAIT|MSG_NOSIGNAL);
|
|
|
|
|
}
|
|
|
|
|
#endif /* IP_PKTINFO || IP_RECVDSTADDR || IPV6_RECVPKTINFO */
|
2022-11-21 08:48:57 -05:00
|
|
|
else {
|
|
|
|
|
ret = sendto(qc->li->rx.fd, b_peek(buf, b_head_ofs(buf)), sz,
|
|
|
|
|
MSG_DONTWAIT|MSG_NOSIGNAL,
|
|
|
|
|
(struct sockaddr *)&qc->peer_addr,
|
|
|
|
|
get_addr_len(&qc->peer_addr));
|
|
|
|
|
}
|
2022-08-03 14:52:20 -04:00
|
|
|
} while (ret < 0 && errno == EINTR);
|
2022-02-09 09:43:07 -05:00
|
|
|
|
2023-02-09 14:37:26 -05:00
|
|
|
if (ret < 0) {
|
2022-08-05 05:56:36 -04:00
|
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK ||
|
2023-02-27 11:31:55 -05:00
|
|
|
errno == ENOTCONN || errno == EINPROGRESS) {
|
2022-08-05 05:56:36 -04:00
|
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
2023-05-24 05:10:19 -04:00
|
|
|
qc->cntrs.socket_full++;
|
2022-08-05 05:56:36 -04:00
|
|
|
else
|
2023-05-24 05:10:19 -04:00
|
|
|
qc->cntrs.sendto_err++;
|
MEDIUM: quic: improve fatal error handling on send
Send is conducted through qc_send_ppkts() for a QUIC connection. There
is two types of error which can be encountered on sendto() or affiliated
syscalls :
* transient error. In this case, sending is simulated with the remaining
data and retransmission process is used to have the opportunity to
retry emission
* fatal error. If this happens, the connection should be closed as soon
as possible. This is done via qc_kill_conn() function. Until this
patch, only ECONNREFUSED errno was considered as fatal.
Modify the QUIC send API to be able to differentiate transient and fatal
errors more easily. This is done by fixing the return value of the
sendto() wrapper qc_snd_buf() :
* on fatal error, a negative error code is returned. This is now the
case for every errno except EAGAIN, EWOULDBLOCK, ENOTCONN, EINPROGRESS
and EBADF.
* on a transient error, 0 is returned. This is the case for the listed
errno values above and also if a partial send has been conducted by
the kernel.
* on success, the return value of sendto() syscall is returned.
This commit will be useful to be able to handle transient error with a
quic-conn owned socket. In this case, the socket should be subscribed to
the poller and no simulated send will be conducted.
This commit allows errno management to be confined in the quic-sock
module which is a nice cleanup.
On a final note, EBADF should be considered as fatal. This will be the
subject of a next commit.
This should be backported up to 2.7.
2023-02-23 05:18:38 -05:00
|
|
|
|
|
|
|
|
/* transient error */
|
2023-02-28 09:11:09 -05:00
|
|
|
fd_want_send(qc->fd);
|
|
|
|
|
fd_cant_send(qc->fd);
|
MEDIUM: quic: improve fatal error handling on send
Send is conducted through qc_send_ppkts() for a QUIC connection. There
is two types of error which can be encountered on sendto() or affiliated
syscalls :
* transient error. In this case, sending is simulated with the remaining
data and retransmission process is used to have the opportunity to
retry emission
* fatal error. If this happens, the connection should be closed as soon
as possible. This is done via qc_kill_conn() function. Until this
patch, only ECONNREFUSED errno was considered as fatal.
Modify the QUIC send API to be able to differentiate transient and fatal
errors more easily. This is done by fixing the return value of the
sendto() wrapper qc_snd_buf() :
* on fatal error, a negative error code is returned. This is now the
case for every errno except EAGAIN, EWOULDBLOCK, ENOTCONN, EINPROGRESS
and EBADF.
* on a transient error, 0 is returned. This is the case for the listed
errno values above and also if a partial send has been conducted by
the kernel.
* on success, the return value of sendto() syscall is returned.
This commit will be useful to be able to handle transient error with a
quic-conn owned socket. In this case, the socket should be subscribed to
the poller and no simulated send will be conducted.
This commit allows errno management to be confined in the quic-sock
module which is a nice cleanup.
On a final note, EBADF should be considered as fatal. This will be the
subject of a next commit.
This should be backported up to 2.7.
2023-02-23 05:18:38 -05:00
|
|
|
TRACE_PRINTF(TRACE_LEVEL_USER, QUIC_EV_CONN_SPPKTS, qc, 0, 0, 0,
|
|
|
|
|
"UDP send failure errno=%d (%s)", errno, strerror(errno));
|
|
|
|
|
return 0;
|
2022-08-05 05:56:36 -04:00
|
|
|
}
|
MEDIUM: quic: improve fatal error handling on send
Send is conducted through qc_send_ppkts() for a QUIC connection. There
is two types of error which can be encountered on sendto() or affiliated
syscalls :
* transient error. In this case, sending is simulated with the remaining
data and retransmission process is used to have the opportunity to
retry emission
* fatal error. If this happens, the connection should be closed as soon
as possible. This is done via qc_kill_conn() function. Until this
patch, only ECONNREFUSED errno was considered as fatal.
Modify the QUIC send API to be able to differentiate transient and fatal
errors more easily. This is done by fixing the return value of the
sendto() wrapper qc_snd_buf() :
* on fatal error, a negative error code is returned. This is now the
case for every errno except EAGAIN, EWOULDBLOCK, ENOTCONN, EINPROGRESS
and EBADF.
* on a transient error, 0 is returned. This is the case for the listed
errno values above and also if a partial send has been conducted by
the kernel.
* on success, the return value of sendto() syscall is returned.
This commit will be useful to be able to handle transient error with a
quic-conn owned socket. In this case, the socket should be subscribed to
the poller and no simulated send will be conducted.
This commit allows errno management to be confined in the quic-sock
module which is a nice cleanup.
On a final note, EBADF should be considered as fatal. This will be the
subject of a next commit.
This should be backported up to 2.7.
2023-02-23 05:18:38 -05:00
|
|
|
else {
|
|
|
|
|
/* unrecoverable error */
|
2023-05-24 05:10:19 -04:00
|
|
|
qc->cntrs.sendto_err_unknown++;
|
MEDIUM: quic: improve fatal error handling on send
Send is conducted through qc_send_ppkts() for a QUIC connection. There
is two types of error which can be encountered on sendto() or affiliated
syscalls :
* transient error. In this case, sending is simulated with the remaining
data and retransmission process is used to have the opportunity to
retry emission
* fatal error. If this happens, the connection should be closed as soon
as possible. This is done via qc_kill_conn() function. Until this
patch, only ECONNREFUSED errno was considered as fatal.
Modify the QUIC send API to be able to differentiate transient and fatal
errors more easily. This is done by fixing the return value of the
sendto() wrapper qc_snd_buf() :
* on fatal error, a negative error code is returned. This is now the
case for every errno except EAGAIN, EWOULDBLOCK, ENOTCONN, EINPROGRESS
and EBADF.
* on a transient error, 0 is returned. This is the case for the listed
errno values above and also if a partial send has been conducted by
the kernel.
* on success, the return value of sendto() syscall is returned.
This commit will be useful to be able to handle transient error with a
quic-conn owned socket. In this case, the socket should be subscribed to
the poller and no simulated send will be conducted.
This commit allows errno management to be confined in the quic-sock
module which is a nice cleanup.
On a final note, EBADF should be considered as fatal. This will be the
subject of a next commit.
This should be backported up to 2.7.
2023-02-23 05:18:38 -05:00
|
|
|
TRACE_PRINTF(TRACE_LEVEL_USER, QUIC_EV_CONN_SPPKTS, qc, 0, 0, 0,
|
|
|
|
|
"UDP send failure errno=%d (%s)", errno, strerror(errno));
|
|
|
|
|
return -1;
|
2022-08-05 05:56:36 -04:00
|
|
|
}
|
2022-08-03 14:52:20 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-09 14:37:26 -05:00
|
|
|
if (ret != sz)
|
MEDIUM: quic: improve fatal error handling on send
Send is conducted through qc_send_ppkts() for a QUIC connection. There
is two types of error which can be encountered on sendto() or affiliated
syscalls :
* transient error. In this case, sending is simulated with the remaining
data and retransmission process is used to have the opportunity to
retry emission
* fatal error. If this happens, the connection should be closed as soon
as possible. This is done via qc_kill_conn() function. Until this
patch, only ECONNREFUSED errno was considered as fatal.
Modify the QUIC send API to be able to differentiate transient and fatal
errors more easily. This is done by fixing the return value of the
sendto() wrapper qc_snd_buf() :
* on fatal error, a negative error code is returned. This is now the
case for every errno except EAGAIN, EWOULDBLOCK, ENOTCONN, EINPROGRESS
and EBADF.
* on a transient error, 0 is returned. This is the case for the listed
errno values above and also if a partial send has been conducted by
the kernel.
* on success, the return value of sendto() syscall is returned.
This commit will be useful to be able to handle transient error with a
quic-conn owned socket. In this case, the socket should be subscribed to
the poller and no simulated send will be conducted.
This commit allows errno management to be confined in the quic-sock
module which is a nice cleanup.
On a final note, EBADF should be considered as fatal. This will be the
subject of a next commit.
This should be backported up to 2.7.
2023-02-23 05:18:38 -05:00
|
|
|
return 0;
|
2023-02-09 14:37:26 -05:00
|
|
|
|
MEDIUM: quic: improve fatal error handling on send
Send is conducted through qc_send_ppkts() for a QUIC connection. There
is two types of error which can be encountered on sendto() or affiliated
syscalls :
* transient error. In this case, sending is simulated with the remaining
data and retransmission process is used to have the opportunity to
retry emission
* fatal error. If this happens, the connection should be closed as soon
as possible. This is done via qc_kill_conn() function. Until this
patch, only ECONNREFUSED errno was considered as fatal.
Modify the QUIC send API to be able to differentiate transient and fatal
errors more easily. This is done by fixing the return value of the
sendto() wrapper qc_snd_buf() :
* on fatal error, a negative error code is returned. This is now the
case for every errno except EAGAIN, EWOULDBLOCK, ENOTCONN, EINPROGRESS
and EBADF.
* on a transient error, 0 is returned. This is the case for the listed
errno values above and also if a partial send has been conducted by
the kernel.
* on success, the return value of sendto() syscall is returned.
This commit will be useful to be able to handle transient error with a
quic-conn owned socket. In this case, the socket should be subscribed to
the poller and no simulated send will be conducted.
This commit allows errno management to be confined in the quic-sock
module which is a nice cleanup.
On a final note, EBADF should be considered as fatal. This will be the
subject of a next commit.
This should be backported up to 2.7.
2023-02-23 05:18:38 -05:00
|
|
|
return ret;
|
2022-02-09 09:43:07 -05:00
|
|
|
}
|
|
|
|
|
|
2022-11-16 05:01:02 -05:00
|
|
|
/* Receive datagram on <qc> 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. */
|
2023-08-11 10:10:34 -04:00
|
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOTCONN)
|
|
|
|
|
fd_want_recv(qc->fd);
|
|
|
|
|
/* TODO handle other error codes as fatal on the connection. */
|
2022-11-16 05:01:02 -05:00
|
|
|
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.
|
|
|
|
|
*/
|
2022-10-05 11:56:08 -04:00
|
|
|
struct quic_receiver_buf *rxbuf;
|
|
|
|
|
struct quic_dgram *tmp_dgram;
|
|
|
|
|
unsigned char *rxbuf_tail;
|
2023-08-04 03:57:04 -04:00
|
|
|
size_t cspace;
|
2022-10-05 11:56:08 -04:00
|
|
|
|
|
|
|
|
TRACE_STATE("datagram for other connection on quic-conn socket, requeue it", QUIC_EV_CONN_RCV, qc);
|
|
|
|
|
|
|
|
|
|
rxbuf = MT_LIST_POP(&l->rx.rxbuf_list, typeof(rxbuf), rxbuf_el);
|
2023-08-04 09:34:34 -04:00
|
|
|
ALREADY_CHECKED(rxbuf);
|
2023-08-04 03:57:04 -04:00
|
|
|
cspace = b_contig_space(&rxbuf->buf);
|
2022-10-05 11:56:08 -04:00
|
|
|
|
|
|
|
|
tmp_dgram = quic_rxbuf_purge_dgrams(rxbuf);
|
|
|
|
|
pool_free(pool_head_quic_dgram, tmp_dgram);
|
|
|
|
|
|
2023-08-04 03:57:04 -04:00
|
|
|
/* Insert a fake datagram if space wraps to consume it. */
|
|
|
|
|
if (cspace < new_dgram->len && b_space_wraps(&rxbuf->buf)) {
|
|
|
|
|
struct quic_dgram *fake_dgram = pool_alloc(pool_head_quic_dgram);
|
|
|
|
|
if (!fake_dgram) {
|
|
|
|
|
/* TODO count lost datagrams */
|
2023-08-04 09:37:29 -04:00
|
|
|
MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->rxbuf_el);
|
2023-08-04 03:57:04 -04:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fake_dgram->buf = NULL;
|
|
|
|
|
fake_dgram->len = cspace;
|
|
|
|
|
LIST_APPEND(&rxbuf->dgram_list, &fake_dgram->recv_list);
|
|
|
|
|
b_add(&rxbuf->buf, cspace);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Recheck contig space after fake datagram insert. */
|
2022-10-05 11:56:08 -04:00
|
|
|
if (b_contig_space(&rxbuf->buf) < new_dgram->len) {
|
|
|
|
|
/* TODO count lost datagrams */
|
|
|
|
|
MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->rxbuf_el);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rxbuf_tail = (unsigned char *)b_tail(&rxbuf->buf);
|
|
|
|
|
__b_putblk(&rxbuf->buf, (char *)dgram_buf, new_dgram->len);
|
2023-05-11 14:43:28 -04:00
|
|
|
if (!quic_lstnr_dgram_dispatch(rxbuf_tail, ret, l, &saddr, &daddr,
|
2022-10-05 11:56:08 -04:00
|
|
|
new_dgram, &rxbuf->dgram_list)) {
|
|
|
|
|
/* TODO count lost datagrams. */
|
|
|
|
|
b_sub(&buf, ret);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* datagram must not be freed as it was requeued. */
|
|
|
|
|
new_dgram = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MT_LIST_APPEND(&l->rx.rxbuf_list, &rxbuf->rxbuf_el);
|
|
|
|
|
continue;
|
2022-11-16 05:01:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-24 11:08:43 -04:00
|
|
|
/* Allocate a socket file-descriptor specific for QUIC connection <qc>.
|
|
|
|
|
* Endpoint addresses are specified by the two following arguments : <src> is
|
|
|
|
|
* the local address and <dst> is the remote one.
|
|
|
|
|
*
|
|
|
|
|
* Return the socket FD or a negative error code. On error, socket is marked as
|
|
|
|
|
* uninitialized.
|
|
|
|
|
*/
|
|
|
|
|
void qc_alloc_fd(struct quic_conn *qc, const struct sockaddr_storage *src,
|
|
|
|
|
const struct sockaddr_storage *dst)
|
|
|
|
|
{
|
2023-10-03 10:11:40 -04:00
|
|
|
struct bind_conf *bc = qc->li->bind_conf;
|
|
|
|
|
struct proxy *p = bc->frontend;
|
2022-10-24 11:08:43 -04:00
|
|
|
int fd = -1;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
/* Must not happen. */
|
|
|
|
|
BUG_ON(src->ss_family != dst->ss_family);
|
|
|
|
|
|
|
|
|
|
qc_init_fd(qc);
|
|
|
|
|
|
|
|
|
|
fd = socket(src->ss_family, SOCK_DGRAM, 0);
|
|
|
|
|
if (fd < 0)
|
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
|
|
if (fd >= global.maxsock) {
|
|
|
|
|
send_log(p, LOG_EMERG,
|
|
|
|
|
"Proxy %s reached the configured maximum connection limit. Please check the global 'maxconn' value.\n",
|
|
|
|
|
p->id);
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
|
|
switch (src->ss_family) {
|
|
|
|
|
case AF_INET:
|
|
|
|
|
#if defined(IP_PKTINFO)
|
|
|
|
|
ret = setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
|
|
|
|
|
#elif defined(IP_RECVDSTADDR)
|
|
|
|
|
ret = setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &one, sizeof(one));
|
|
|
|
|
#endif /* IP_PKTINFO || IP_RECVDSTADDR */
|
|
|
|
|
break;
|
|
|
|
|
case AF_INET6:
|
|
|
|
|
#ifdef IPV6_RECVPKTINFO
|
|
|
|
|
ret = setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
|
|
|
|
|
#endif
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
|
|
ret = bind(fd, (struct sockaddr *)src, get_addr_len(src));
|
2023-10-03 10:11:40 -04:00
|
|
|
if (ret < 0) {
|
|
|
|
|
if (errno == EACCES) {
|
|
|
|
|
if (!quic_bind_eacces_warn) {
|
|
|
|
|
send_log(p, LOG_WARNING,
|
|
|
|
|
"Permission error on QUIC socket binding for proxy %s. Consider using setcap cap_net_bind_service (Linux only) or running as root.\n",
|
|
|
|
|
p->id);
|
|
|
|
|
quic_bind_eacces_warn = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Fallback to listener socket for this receiver instance. */
|
|
|
|
|
HA_ATOMIC_STORE(&qc->li->rx.quic_mode, QUIC_SOCK_MODE_LSTNR);
|
|
|
|
|
}
|
2022-10-24 11:08:43 -04:00
|
|
|
goto err;
|
2023-10-03 10:11:40 -04:00
|
|
|
}
|
2022-10-24 11:08:43 -04:00
|
|
|
|
|
|
|
|
ret = connect(fd, (struct sockaddr *)dst, get_addr_len(dst));
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
|
|
qc->fd = fd;
|
|
|
|
|
fd_set_nonblock(fd);
|
2022-10-24 11:40:37 -04:00
|
|
|
fd_insert(fd, qc, quic_conn_sock_fd_iocb, tgid, ti->ltid_bit);
|
|
|
|
|
fd_want_recv(fd);
|
2022-10-24 11:08:43 -04:00
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
if (fd >= 0)
|
|
|
|
|
close(fd);
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-01 10:20:06 -05:00
|
|
|
/* Release socket file-descriptor specific for QUIC connection <qc>. Set
|
|
|
|
|
* <reinit> if socket should be reinitialized after address migration.
|
|
|
|
|
*/
|
|
|
|
|
void qc_release_fd(struct quic_conn *qc, int reinit)
|
2022-10-24 11:08:43 -04:00
|
|
|
{
|
2022-10-24 11:40:37 -04:00
|
|
|
if (qc_test_fd(qc)) {
|
|
|
|
|
fd_delete(qc->fd);
|
2022-10-24 11:08:43 -04:00
|
|
|
qc->fd = DEAD_FD_MAGIC;
|
2022-12-01 10:20:06 -05:00
|
|
|
|
|
|
|
|
if (reinit)
|
|
|
|
|
qc_init_fd(qc);
|
2022-10-24 11:40:37 -04:00
|
|
|
}
|
2022-10-24 11:08:43 -04:00
|
|
|
}
|
2022-01-19 09:46:11 -05:00
|
|
|
|
2023-04-11 08:42:31 -04:00
|
|
|
/* Wrapper for fd_want_recv(). Safe even if connection does not used its owned
|
|
|
|
|
* socket.
|
|
|
|
|
*/
|
|
|
|
|
void qc_want_recv(struct quic_conn *qc)
|
|
|
|
|
{
|
|
|
|
|
if (qc_test_fd(qc))
|
|
|
|
|
fd_want_recv(qc->fd);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-19 09:46:11 -05:00
|
|
|
/*********************** QUIC accept queue management ***********************/
|
|
|
|
|
/* per-thread accept queues */
|
|
|
|
|
struct quic_accept_queue *quic_accept_queues;
|
|
|
|
|
|
2022-01-19 10:01:05 -05:00
|
|
|
/* Install <qc> on the queue ready to be accepted. The queue task is then woken
|
2022-01-28 15:43:48 -05:00
|
|
|
* up. If <qc> accept is already scheduled or done, nothing is done.
|
2022-01-19 10:01:05 -05:00
|
|
|
*/
|
|
|
|
|
void quic_accept_push_qc(struct quic_conn *qc)
|
|
|
|
|
{
|
2023-04-13 05:48:38 -04:00
|
|
|
struct quic_accept_queue *queue = &quic_accept_queues[tid];
|
2023-04-21 04:46:45 -04:00
|
|
|
struct li_per_thread *lthr = &qc->li->per_thr[ti->ltid];
|
2022-01-19 10:01:05 -05:00
|
|
|
|
2022-01-28 15:43:48 -05:00
|
|
|
/* early return if accept is already in progress/done for this
|
|
|
|
|
* connection
|
|
|
|
|
*/
|
2022-03-28 11:10:31 -04:00
|
|
|
if (qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED)
|
2022-01-28 15:43:48 -05:00
|
|
|
return;
|
|
|
|
|
|
2022-01-19 10:01:05 -05:00
|
|
|
BUG_ON(MT_LIST_INLIST(&qc->accept_list));
|
2023-11-08 08:29:31 -05:00
|
|
|
HA_ATOMIC_INC(&qc->li->rx.quic_curr_accept);
|
2022-01-19 10:01:05 -05:00
|
|
|
|
2022-03-28 11:10:31 -04:00
|
|
|
qc->flags |= QUIC_FL_CONN_ACCEPT_REGISTERED;
|
2022-01-19 10:01:05 -05:00
|
|
|
/* 1. insert the listener in the accept queue
|
|
|
|
|
*
|
|
|
|
|
* Use TRY_APPEND as there is a possible race even with INLIST if
|
|
|
|
|
* multiple threads try to add the same listener instance from several
|
|
|
|
|
* quic_conn.
|
|
|
|
|
*/
|
|
|
|
|
if (!MT_LIST_INLIST(&(lthr->quic_accept.list)))
|
|
|
|
|
MT_LIST_TRY_APPEND(&queue->listeners, &(lthr->quic_accept.list));
|
|
|
|
|
|
|
|
|
|
/* 2. insert the quic_conn in the listener per-thread queue. */
|
|
|
|
|
MT_LIST_APPEND(<hr->quic_accept.conns, &qc->accept_list);
|
|
|
|
|
|
|
|
|
|
/* 3. wake up the queue tasklet */
|
2023-04-13 05:48:38 -04:00
|
|
|
tasklet_wakeup(quic_accept_queues[tid].tasklet);
|
2022-01-19 10:01:05 -05:00
|
|
|
}
|
|
|
|
|
|
2022-01-19 09:46:11 -05:00
|
|
|
/* Tasklet handler to accept QUIC connections. Call listener_accept on every
|
|
|
|
|
* listener instances registered in the accept queue.
|
|
|
|
|
*/
|
2022-09-08 09:12:59 -04:00
|
|
|
struct task *quic_accept_run(struct task *t, void *ctx, unsigned int i)
|
2022-01-19 09:46:11 -05:00
|
|
|
{
|
|
|
|
|
struct li_per_thread *lthr;
|
2023-09-15 11:09:45 -04:00
|
|
|
struct mt_list *elt1, elt2;
|
2022-01-19 09:46:11 -05:00
|
|
|
struct quic_accept_queue *queue = &quic_accept_queues[tid];
|
|
|
|
|
|
2023-09-15 11:09:45 -04:00
|
|
|
mt_list_for_each_entry_safe(lthr, &queue->listeners, quic_accept.list, elt1, elt2) {
|
2022-01-19 09:46:11 -05:00
|
|
|
listener_accept(lthr->li);
|
BUG/MINOR: quic: Missing listener accept queue tasklet wakeups
This bug was revealed by h2load tests run as follows:
h2load -t 4 --npn-list h3 -c 64 -m 16 -n 16384 -v https://127.0.0.1:4443/
This open (-c) 64 QUIC connections (-n) 16384 h3 requets from (-t) 4 threads, i.e.
256 requests by connection. Such tests could not always pass and often ended with
such results displays by h2load:
finished in 53.74s, 38.11 req/s, 493.78KB/s
requests: 16384 total, 2944 started, 2048 done, 2048 succeeded, 14336
failed, 14336 errored, 0 timeout
status codes: 2048 2xx, 0 3xx, 0 4xx, 0 5xx
traffic: 25.92MB (27174537) total, 102.00KB (104448) headers (space
savings 1.92%), 25.80MB (27053569) data
UDP datagram: 3883 sent, 24330 received
min max mean sd ± sd
time for request: 48.75ms 502.86ms 134.12ms 75.80ms 92.68%
time for connect: 20.94ms 331.24ms 189.59ms 84.81ms 59.38%
time to 1st byte: 394.36ms 417.01ms 406.72ms 9.14ms 75.00%
req/s : 0.00 115.45 14.30 38.13 87.50%
The number of successful requests was always a multiple of 256.
Activating the traces also shew that some connections were blocked after having
successfully completed their handshakes due to the fact that the mux. The mux
is started upon the acceptation of the connection.
Under heavy load, some connections were never accepted. From the moment where
more than 4 (MAXACCEPT) connections were enqueued before a listener could be
woken up to accept at most 4 connections, the remaining connections were not
accepted ore lately at the second listener tasklet wakeup.
Add a call to tasklet_wakeup() to the accept list tasklet of the listeners to
wake up it if there are remaining connections to accept after having called
listener_accept(). In this case the listener must not be removed of this
accept list, if not at the next call it will not accept anything more.
Must be backported to 2.7 and 2.6.
2023-03-10 07:34:30 -05:00
|
|
|
if (!MT_LIST_ISEMPTY(<hr->quic_accept.conns))
|
|
|
|
|
tasklet_wakeup((struct tasklet*)t);
|
|
|
|
|
else
|
2023-09-15 11:09:45 -04:00
|
|
|
MT_LIST_DELETE_SAFE(elt1);
|
2022-01-19 09:46:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-06 10:34:38 -05:00
|
|
|
/* Returns the maximum number of QUIC connections waiting for handshake to
|
2023-11-08 08:29:31 -05:00
|
|
|
* complete in parallel on listener <l> instance. This is directly based on
|
|
|
|
|
* listener backlog value.
|
2023-11-06 10:34:38 -05:00
|
|
|
*/
|
|
|
|
|
int quic_listener_max_handshake(const struct listener *l)
|
|
|
|
|
{
|
2023-11-08 08:29:31 -05:00
|
|
|
return listener_backlog(l) / 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns the value which is considered as the maximum number of QUIC
|
|
|
|
|
* connections waiting to be accepted for listener <l> instance. This is
|
|
|
|
|
* directly based on listener backlog value.
|
|
|
|
|
*/
|
|
|
|
|
int quic_listener_max_accept(const struct listener *l)
|
|
|
|
|
{
|
|
|
|
|
return listener_backlog(l) / 2;
|
2023-11-06 10:34:38 -05:00
|
|
|
}
|
|
|
|
|
|
2022-01-19 09:46:11 -05:00
|
|
|
static int quic_alloc_accept_queues(void)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
2022-06-01 15:58:37 -04:00
|
|
|
quic_accept_queues = calloc(global.nbthread,
|
|
|
|
|
sizeof(*quic_accept_queues));
|
2022-01-19 09:46:11 -05:00
|
|
|
if (!quic_accept_queues) {
|
|
|
|
|
ha_alert("Failed to allocate the quic accept queues.\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < global.nbthread; ++i) {
|
|
|
|
|
struct tasklet *task;
|
|
|
|
|
if (!(task = tasklet_new())) {
|
|
|
|
|
ha_alert("Failed to allocate the quic accept queue on thread %d.\n", i);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tasklet_set_tid(task, i);
|
|
|
|
|
task->process = quic_accept_run;
|
|
|
|
|
quic_accept_queues[i].tasklet = task;
|
|
|
|
|
|
|
|
|
|
MT_LIST_INIT(&quic_accept_queues[i].listeners);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
REGISTER_POST_CHECK(quic_alloc_accept_queues);
|
|
|
|
|
|
|
|
|
|
static int quic_deallocate_accept_queues(void)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (quic_accept_queues) {
|
|
|
|
|
for (i = 0; i < global.nbthread; ++i)
|
|
|
|
|
tasklet_free(quic_accept_queues[i].tasklet);
|
|
|
|
|
free(quic_accept_queues);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
REGISTER_POST_DEINIT(quic_deallocate_accept_queues);
|