Compare commits
33 Commits
master
...
20250526-q
Author | SHA1 | Date | |
---|---|---|---|
|
8f79ac238c | ||
|
c6d79ebede | ||
|
b57eea3bf3 | ||
|
979b41fe5f | ||
|
f363190bd7 | ||
|
21e2c2e9dc | ||
|
ad5b52e07a | ||
|
acbb56eac6 | ||
|
cfa82a8606 | ||
|
7fc69b202a | ||
|
a0d2a4fcfd | ||
|
74a35d94ea | ||
|
c7accba1db | ||
|
9d515eb7f3 | ||
|
fb5ce7fd02 | ||
|
1f4cb27157 | ||
|
bfa6885c2f | ||
|
7d3f60f079 | ||
|
73054af3fc | ||
|
d5a2e9b44e | ||
|
5517b9c521 | ||
|
e4f7a1fcde | ||
|
7f89f1e451 | ||
|
b99ce3c985 | ||
|
abd35e9b17 | ||
|
d5669868e6 | ||
|
7265955aa4 | ||
|
cc5ae9a29b | ||
|
e46e8e2bd3 | ||
|
efbac62ded | ||
|
02f85dd6af | ||
|
e124a66c90 | ||
|
438bb236cf |
@ -144,6 +144,7 @@ struct qc_stream_rxbuf {
|
||||
struct qcs {
|
||||
struct qcc *qcc;
|
||||
struct sedesc *sd;
|
||||
struct session *sess;
|
||||
uint32_t flags; /* QC_SF_* */
|
||||
enum qcs_state st; /* QC_SS_* state */
|
||||
void *ctx; /* app-ops context */
|
||||
|
@ -228,6 +228,9 @@ struct quic_version {
|
||||
extern const struct quic_version quic_versions[];
|
||||
extern const size_t quic_versions_nb;
|
||||
extern const struct quic_version *preferred_version;
|
||||
extern const struct quic_version *quic_version_draft_29;
|
||||
extern const struct quic_version *quic_version_1;
|
||||
extern const struct quic_version *quic_version_2;
|
||||
|
||||
/* unused: 0x01 */
|
||||
/* Flag the packet number space as requiring an ACK frame to be sent. */
|
||||
@ -305,6 +308,7 @@ struct qcc_app_ops;
|
||||
/* Number of received bytes. */ \
|
||||
uint64_t rx; \
|
||||
} bytes; \
|
||||
size_t max_udp_payload; \
|
||||
/* First DCID used by client on its Initial packet. */ \
|
||||
struct quic_cid odcid; \
|
||||
/* DCID of our endpoint - not updated when a new DCID is used */ \
|
||||
@ -315,7 +319,7 @@ struct qcc_app_ops;
|
||||
* with a connection \
|
||||
*/ \
|
||||
struct eb_root *cids; \
|
||||
struct listener *li; /* only valid for frontend connections */ \
|
||||
enum obj_type *target; \
|
||||
/* Idle timer task */ \
|
||||
struct task *idle_timer_task; \
|
||||
unsigned int idle_expire; \
|
||||
@ -456,6 +460,7 @@ struct quic_conn_closed {
|
||||
#define QUIC_FL_CONN_HPKTNS_DCD (1U << 16) /* Handshake packet number space discarded */
|
||||
#define QUIC_FL_CONN_PEER_VALIDATED_ADDR (1U << 17) /* Peer address is considered as validated for this connection. */
|
||||
#define QUIC_FL_CONN_NO_TOKEN_RCVD (1U << 18) /* Client dit not send any token */
|
||||
#define QUIC_FL_CONN_SCID_RECEIVED (1U << 19) /* (client only: first Initial received. */
|
||||
/* gap here */
|
||||
#define QUIC_FL_CONN_TO_KILL (1U << 24) /* Unusable connection, to be killed */
|
||||
#define QUIC_FL_CONN_TX_TP_RECEIVED (1U << 25) /* Peer transport parameters have been received (used for the transmitting part) */
|
||||
|
@ -69,7 +69,8 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
struct quic_connection_id *conn_id,
|
||||
struct sockaddr_storage *local_addr,
|
||||
struct sockaddr_storage *peer_addr,
|
||||
int server, int token, void *owner);
|
||||
int token, void *owner,
|
||||
struct connection *conn);
|
||||
int quic_build_post_handshake_frames(struct quic_conn *qc);
|
||||
const struct quic_version *qc_supported_version(uint32_t version);
|
||||
int quic_peer_validated_addr(struct quic_conn *qc);
|
||||
@ -163,6 +164,22 @@ static inline void quic_free_ncbuf(struct ncbuf *ncbuf)
|
||||
*ncbuf = NCBUF_NULL;
|
||||
}
|
||||
|
||||
/* Return the address of the QUIC counters attached to the proxy of
|
||||
* the owner of the connection whose object type address is <o> for
|
||||
* listener and servers, or NULL for others object type.
|
||||
*/
|
||||
static inline void *qc_counters(enum obj_type *o, const struct stats_module *m)
|
||||
{
|
||||
struct proxy *p;
|
||||
struct listener *l = objt_listener(o);
|
||||
struct server *s = objt_server(o);
|
||||
|
||||
p = l ? l->bind_conf->frontend :
|
||||
s ? s->proxy : NULL;
|
||||
|
||||
return p ? EXTRA_COUNTERS_GET(p->extra_counters_fe, m) : NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include <haproxy/quic_rx-t.h>
|
||||
|
||||
int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
|
||||
struct listener *li);
|
||||
enum obj_type *obj_type);
|
||||
int qc_treat_rx_pkts(struct quic_conn *qc);
|
||||
int qc_parse_hd_form(struct quic_rx_packet *pkt,
|
||||
unsigned char **pos, const unsigned char *end);
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <haproxy/connection-t.h>
|
||||
#include <haproxy/fd-t.h>
|
||||
#include <haproxy/listener-t.h>
|
||||
#include <haproxy/obj_type.h>
|
||||
#include <haproxy/quic_conn-t.h>
|
||||
#include <haproxy/quic_sock-t.h>
|
||||
|
||||
@ -78,7 +79,8 @@ static inline char qc_test_fd(struct quic_conn *qc)
|
||||
*/
|
||||
static inline int qc_fd(struct quic_conn *qc)
|
||||
{
|
||||
return qc_test_fd(qc) ? qc->fd : qc->li->rx.fd;
|
||||
/* TODO: check this: For backends, qc->fd is always initialized */
|
||||
return qc_test_fd(qc) ? qc->fd : __objt_listener(qc->target)->rx.fd;
|
||||
}
|
||||
|
||||
/* Try to increment <l> handshake current counter. If listener limit is
|
||||
|
@ -34,7 +34,8 @@
|
||||
#include <haproxy/ssl_sock-t.h>
|
||||
|
||||
int ssl_quic_initial_ctx(struct bind_conf *bind_conf);
|
||||
int qc_alloc_ssl_sock_ctx(struct quic_conn *qc);
|
||||
SSL_CTX *ssl_quic_srv_new_ssl_ctx(void);
|
||||
int qc_alloc_ssl_sock_ctx(struct quic_conn *qc, struct connection *conn);
|
||||
int qc_ssl_provide_all_quic_data(struct quic_conn *qc, struct ssl_sock_ctx *ctx);
|
||||
int quic_ssl_set_tls_cbs(SSL *ssl);
|
||||
|
||||
|
@ -26,6 +26,9 @@ int qc_lstnr_params_init(struct quic_conn *qc,
|
||||
const unsigned char *dcid, size_t dcidlen,
|
||||
const unsigned char *scid, size_t scidlen,
|
||||
const struct quic_cid *token_odcid);
|
||||
void qc_srv_params_init(struct quic_conn *qc,
|
||||
const struct quic_transport_params *srv_params,
|
||||
const unsigned char *scid, size_t scidlen);
|
||||
|
||||
/* Dump <cid> transport parameter connection ID value if present (non null length).
|
||||
* Used only for debugging purposes.
|
||||
|
@ -478,6 +478,9 @@ struct server {
|
||||
char *alpn_str; /* ALPN protocol string */
|
||||
int alpn_len; /* ALPN protocol string length */
|
||||
} ssl_ctx;
|
||||
#ifdef USE_QUIC
|
||||
struct quic_transport_params quic_params; /* QUIC transport parameters */
|
||||
#endif
|
||||
struct resolv_srvrq *srvrq; /* Pointer representing the DNS SRV requeest, if any */
|
||||
struct list srv_rec_item; /* to attach server to a srv record item */
|
||||
struct list ip_rec_item; /* to attach server to a A or AAAA record item */
|
||||
|
@ -343,6 +343,16 @@ static inline void srv_detach(struct server *srv)
|
||||
}
|
||||
}
|
||||
|
||||
static inline int srv_is_quic(const struct server *srv)
|
||||
{
|
||||
#ifdef USE_QUIC
|
||||
return srv->addr_type.proto_type == PROTO_TYPE_DGRAM &&
|
||||
srv->addr_type.xprt_type == PROTO_TYPE_STREAM;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* _HAPROXY_SERVER_H */
|
||||
|
||||
/*
|
||||
|
@ -28,9 +28,11 @@
|
||||
#include <haproxy/api.h>
|
||||
#include <haproxy/connection-t.h>
|
||||
#include <haproxy/listener-t.h>
|
||||
#include <haproxy/protocol-t.h>
|
||||
#include <haproxy/sock-t.h>
|
||||
|
||||
int sock_create_server_socket(struct connection *conn, struct proxy *be, int *stream_err);
|
||||
int sock_create_server_socket(struct connection *conn, struct proxy *be,
|
||||
enum proto_type proto_type, int sock_type, int *stream_err);
|
||||
void sock_enable(struct receiver *rx);
|
||||
void sock_disable(struct receiver *rx);
|
||||
void sock_unbind(struct receiver *rx);
|
||||
|
@ -62,7 +62,7 @@ struct ckch_inst *ckch_inst_new();
|
||||
int ckch_inst_new_load_store(const char *path, struct ckch_store *ckchs, struct bind_conf *bind_conf,
|
||||
struct ssl_bind_conf *ssl_conf, char **sni_filter, int fcount, int is_default, struct ckch_inst **ckchi, char **err);
|
||||
int ckch_inst_new_load_srv_store(const char *path, struct ckch_store *ckchs,
|
||||
struct ckch_inst **ckchi, char **err);
|
||||
struct ckch_inst **ckchi, char **err, int is_quic);
|
||||
int ckch_inst_rebuild(struct ckch_store *ckch_store, struct ckch_inst *ckchi,
|
||||
struct ckch_inst **new_inst, char **err);
|
||||
|
||||
|
@ -1992,7 +1992,9 @@ int connect_server(struct stream *s)
|
||||
/* set the correct protocol on the output stream connector */
|
||||
|
||||
if (srv) {
|
||||
if (conn_prepare(srv_conn, protocol_lookup(srv_conn->dst->ss_family, PROTO_TYPE_STREAM, srv->alt_proto), srv->xprt)) {
|
||||
struct protocol *proto = protocol_lookup(srv_conn->dst->ss_family, srv->addr_type.proto_type, srv->alt_proto);
|
||||
|
||||
if (conn_prepare(srv_conn, proto, srv->xprt)) {
|
||||
conn_free(srv_conn);
|
||||
return SF_ERR_INTERNAL;
|
||||
}
|
||||
|
@ -3767,6 +3767,8 @@ out_uri_auth_compat:
|
||||
((newsrv->flags & SRV_F_DEFSRV_USE_SSL) && newsrv->use_ssl != 1)) {
|
||||
if (xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->prepare_srv)
|
||||
cfgerr += xprt_get(XPRT_SSL)->prepare_srv(newsrv);
|
||||
else if (xprt_get(XPRT_QUIC) && xprt_get(XPRT_QUIC)->prepare_srv)
|
||||
cfgerr += xprt_get(XPRT_QUIC)->prepare_srv(newsrv);
|
||||
}
|
||||
|
||||
if ((newsrv->flags & SRV_F_FASTOPEN) &&
|
||||
|
@ -1423,7 +1423,8 @@ static int cli_io_handler_show_fd(struct appctx *appctx)
|
||||
#if defined(USE_QUIC)
|
||||
else if (fdt.iocb == quic_conn_sock_fd_iocb) {
|
||||
qc = fdtab[fd].owner;
|
||||
li = qc ? qc->li : NULL;
|
||||
li = qc ? objt_listener(qc->target) : NULL;
|
||||
sv = qc ? objt_server(qc->target) : NULL;
|
||||
xprt_ctx = qc ? qc->xprt_ctx : NULL;
|
||||
conn = qc ? qc->conn : NULL;
|
||||
xprt = conn ? conn->xprt : NULL; // in fact it's &ssl_quic
|
||||
|
6
src/h3.c
6
src/h3.c
@ -36,6 +36,7 @@
|
||||
#include <haproxy/qmux_http.h>
|
||||
#include <haproxy/qpack-dec.h>
|
||||
#include <haproxy/qpack-enc.h>
|
||||
#include <haproxy/quic_conn.h>
|
||||
#include <haproxy/quic_enc.h>
|
||||
#include <haproxy/quic_fctl.h>
|
||||
#include <haproxy/quic_frame.h>
|
||||
@ -2513,7 +2514,6 @@ static int h3_send_goaway(struct h3c *h3c)
|
||||
static int h3_init(struct qcc *qcc)
|
||||
{
|
||||
struct h3c *h3c;
|
||||
const struct listener *li = __objt_listener(qcc->conn->target);
|
||||
|
||||
TRACE_ENTER(H3_EV_H3C_NEW, qcc->conn);
|
||||
|
||||
@ -2530,9 +2530,7 @@ static int h3_init(struct qcc *qcc)
|
||||
h3c->id_goaway = 0;
|
||||
|
||||
qcc->ctx = h3c;
|
||||
h3c->prx_counters =
|
||||
EXTRA_COUNTERS_GET(li->bind_conf->frontend->extra_counters_fe,
|
||||
&h3_stats_module);
|
||||
h3c->prx_counters = qc_counters(qcc->conn->target, &h3_stats_module);
|
||||
LIST_INIT(&h3c->buf_wait.list);
|
||||
|
||||
TRACE_LEAVE(H3_EV_H3C_NEW, qcc->conn);
|
||||
|
@ -134,6 +134,8 @@ static struct qcs *qcs_new(struct qcc *qcc, uint64_t id, enum qcs_type type)
|
||||
|
||||
qcs->stream = NULL;
|
||||
qcs->qcc = qcc;
|
||||
qcs->sd = NULL;
|
||||
qcs->sess = NULL;
|
||||
qcs->flags = QC_SF_NONE;
|
||||
qcs->st = QC_SS_IDLE;
|
||||
qcs->ctx = NULL;
|
||||
@ -3406,6 +3408,7 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
|
||||
{
|
||||
struct qcc *qcc;
|
||||
struct quic_transport_params *lparams, *rparams;
|
||||
void *conn_ctx = conn->ctx;
|
||||
|
||||
TRACE_ENTER(QMUX_EV_QCC_NEW);
|
||||
|
||||
@ -3534,6 +3537,22 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
|
||||
/* Register conn for idle front closing. This is done once everything is allocated. */
|
||||
if (!conn_is_back(conn))
|
||||
LIST_APPEND(&mux_stopping_data[tid].list, &conn->stopping_list);
|
||||
else {
|
||||
struct qcs *qcs;
|
||||
struct stconn *sc = conn_ctx;
|
||||
|
||||
qcs = qcc_init_stream_local(qcc, 1);
|
||||
if (!qcs) {
|
||||
TRACE_PROTO("Cannot allocate a new locally initiated streeam",
|
||||
QMUX_EV_QCC_NEW|QMUX_EV_QCC_ERR, conn);
|
||||
goto err;
|
||||
}
|
||||
|
||||
sc_attach_mux(sc, qcs, conn);
|
||||
qcs->sd = sc->sedesc;
|
||||
qcs->sess = sess;
|
||||
qcc->nb_sc++;
|
||||
}
|
||||
|
||||
/* init read cycle */
|
||||
tasklet_wakeup(qcc->wait_event.tasklet);
|
||||
@ -4149,6 +4168,6 @@ void qcc_show_quic(struct qcc *qcc)
|
||||
}
|
||||
|
||||
static struct mux_proto_list mux_proto_quic =
|
||||
{ .token = IST("quic"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_FE, .mux = &qmux_ops };
|
||||
{ .token = IST("quic"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &qmux_ops };
|
||||
|
||||
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_quic);
|
||||
|
@ -286,11 +286,11 @@ int quic_connect_server(struct connection *conn, int flags)
|
||||
struct proxy *be;
|
||||
struct conn_src *src;
|
||||
struct sockaddr_storage *addr;
|
||||
struct quic_conn *qc = conn->handle.qc;
|
||||
|
||||
BUG_ON(qc->fd != -1);
|
||||
BUG_ON(!conn->dst);
|
||||
|
||||
conn->flags |= CO_FL_WAIT_L4_CONN; /* connection in progress */
|
||||
|
||||
switch (obj_type(conn->target)) {
|
||||
case OBJ_TYPE_PROXY:
|
||||
be = __objt_proxy(conn->target);
|
||||
@ -306,7 +306,7 @@ int quic_connect_server(struct connection *conn, int flags)
|
||||
}
|
||||
|
||||
/* perform common checks on obtained socket FD, return appropriate Stream Error Flag in case of failure */
|
||||
fd = conn->handle.fd = sock_create_server_socket(conn, be, &stream_err);
|
||||
fd = sock_create_server_socket(conn, be, PROTO_TYPE_DGRAM, SOCK_DGRAM, &stream_err);
|
||||
if (fd == -1)
|
||||
return stream_err;
|
||||
|
||||
@ -413,71 +413,23 @@ int quic_connect_server(struct connection *conn, int flags)
|
||||
}
|
||||
|
||||
if (global.tune.server_sndbuf)
|
||||
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &global.tune.server_sndbuf, sizeof(global.tune.server_sndbuf));
|
||||
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &global.tune.server_sndbuf, sizeof(global.tune.server_sndbuf));
|
||||
|
||||
if (global.tune.server_rcvbuf)
|
||||
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &global.tune.server_rcvbuf, sizeof(global.tune.server_rcvbuf));
|
||||
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &global.tune.server_rcvbuf, sizeof(global.tune.server_rcvbuf));
|
||||
|
||||
addr = (conn->flags & CO_FL_SOCKS4) ? &srv->socks4_addr : conn->dst;
|
||||
if (connect(fd, (const struct sockaddr *)addr, get_addr_len(addr)) == -1) {
|
||||
if (errno == EINPROGRESS || errno == EALREADY) {
|
||||
/* common case, let's wait for connect status */
|
||||
conn->flags |= CO_FL_WAIT_L4_CONN;
|
||||
}
|
||||
else if (errno == EISCONN) {
|
||||
/* should normally not happen but if so, indicates that it's OK */
|
||||
conn->flags &= ~CO_FL_WAIT_L4_CONN;
|
||||
}
|
||||
else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EADDRINUSE || errno == EADDRNOTAVAIL) {
|
||||
char *msg;
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EADDRNOTAVAIL) {
|
||||
msg = "no free ports";
|
||||
conn->err_code = CO_ER_FREE_PORTS;
|
||||
}
|
||||
else {
|
||||
msg = "local address already in use";
|
||||
conn->err_code = CO_ER_ADDR_INUSE;
|
||||
}
|
||||
|
||||
qfprintf(stderr,"Connect() failed for backend %s: %s.\n", be->id, msg);
|
||||
port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
|
||||
fdinfo[fd].port_range = NULL;
|
||||
close(fd);
|
||||
send_log(be, LOG_ERR, "Connect() failed for backend %s: %s.\n", be->id, msg);
|
||||
conn->flags |= CO_FL_ERROR;
|
||||
return SF_ERR_RESOURCE;
|
||||
} else if (errno == ETIMEDOUT) {
|
||||
//qfprintf(stderr,"Connect(): ETIMEDOUT");
|
||||
port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
|
||||
fdinfo[fd].port_range = NULL;
|
||||
close(fd);
|
||||
conn->err_code = CO_ER_SOCK_ERR;
|
||||
conn->flags |= CO_FL_ERROR;
|
||||
return SF_ERR_SRVTO;
|
||||
} else {
|
||||
// (errno == ECONNREFUSED || errno == ENETUNREACH || errno == EACCES || errno == EPERM)
|
||||
//qfprintf(stderr,"Connect(): %d", errno);
|
||||
port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
|
||||
fdinfo[fd].port_range = NULL;
|
||||
close(fd);
|
||||
conn->err_code = CO_ER_SOCK_ERR;
|
||||
conn->flags |= CO_FL_ERROR;
|
||||
return SF_ERR_SRVCL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* connect() == 0, this is great! */
|
||||
conn->flags &= ~CO_FL_WAIT_L4_CONN;
|
||||
port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
|
||||
fdinfo[fd].port_range = NULL;
|
||||
close(fd);
|
||||
conn->flags |= CO_FL_ERROR;
|
||||
return SF_ERR_SRVCL;
|
||||
}
|
||||
|
||||
conn_ctrl_init(conn); /* registers the FD */
|
||||
HA_ATOMIC_OR(&fdtab[fd].state, FD_LINGER_RISK); /* close hard if needed */
|
||||
|
||||
if (conn->flags & CO_FL_WAIT_L4_CONN) {
|
||||
fd_want_send(fd);
|
||||
fd_cant_send(fd);
|
||||
fd_cant_recv(fd);
|
||||
}
|
||||
qc->fd = fd;
|
||||
fd_insert(fd, qc, quic_conn_sock_fd_iocb, tgid, ti->ltid_bit);
|
||||
fd_want_recv(fd);
|
||||
|
||||
return SF_ERR_NONE; /* connection is OK */
|
||||
}
|
||||
|
@ -397,7 +397,7 @@ int tcp_connect_server(struct connection *conn, int flags)
|
||||
|
||||
|
||||
/* perform common checks on obtained socket FD, return appropriate Stream Error Flag in case of failure */
|
||||
fd = conn->handle.fd = sock_create_server_socket(conn, be, &stream_err);
|
||||
fd = conn->handle.fd = sock_create_server_socket(conn, be, PROTO_TYPE_STREAM, SOCK_STREAM, &stream_err);
|
||||
if (fd == -1)
|
||||
return stream_err;
|
||||
|
||||
|
@ -330,7 +330,7 @@ static int uxst_connect_server(struct connection *conn, int flags)
|
||||
}
|
||||
|
||||
/* perform common checks on obtained socket FD, return appropriate Stream Error Flag in case of failure */
|
||||
fd = conn->handle.fd = sock_create_server_socket(conn, be, &stream_err);
|
||||
fd = conn->handle.fd = sock_create_server_socket(conn, be, PROTO_TYPE_STREAM, SOCK_STREAM, &stream_err);
|
||||
if (fd == -1)
|
||||
return stream_err;
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <haproxy/cli.h>
|
||||
#include <haproxy/list.h>
|
||||
#include <haproxy/mux_quic.h>
|
||||
#include <haproxy/quic_conn-t.h>
|
||||
#include <haproxy/quic_conn.h>
|
||||
#include <haproxy/quic_tp.h>
|
||||
#include <haproxy/quic_utils.h>
|
||||
#include <haproxy/tools.h>
|
||||
@ -181,9 +181,11 @@ static void dump_quic_oneline(struct show_quic_ctx *ctx, struct quic_conn *qc)
|
||||
char bufaddr[INET6_ADDRSTRLEN], bufport[6];
|
||||
int ret;
|
||||
unsigned char cid_len;
|
||||
struct listener *l = objt_listener(qc->target);
|
||||
|
||||
ret = chunk_appendf(&trash, "%p[%02u]/%-.12s ", qc, ctx->thr,
|
||||
qc->li->bind_conf->frontend->id);
|
||||
l ? l->bind_conf->frontend->id : __objt_server(qc->target)->id);
|
||||
|
||||
chunk_appendf(&trash, "%*s", 36 - ret, " "); /* align output */
|
||||
|
||||
/* State */
|
||||
|
193
src/quic_conn.c
193
src/quic_conn.c
@ -119,6 +119,10 @@ const struct quic_version quic_versions[] = {
|
||||
},
|
||||
};
|
||||
|
||||
const struct quic_version *quic_version_draft_29 = &quic_versions[0];
|
||||
const struct quic_version *quic_version_1 = &quic_versions[1];
|
||||
const struct quic_version *quic_version_2 = &quic_versions[2];
|
||||
|
||||
/* Function pointers, can be used to compute a hash from first generated CID and to derive new CIDs */
|
||||
uint64_t (*quic_hash64_from_cid)(const unsigned char *cid, int size, const unsigned char *secret, size_t secretlen) = NULL;
|
||||
void (*quic_newcid_from_hash64)(unsigned char *cid, int size, uint64_t hash, const unsigned char *secret, size_t secretlen) = NULL;
|
||||
@ -745,7 +749,7 @@ static struct quic_conn_closed *qc_new_cc_conn(struct quic_conn *qc)
|
||||
cc_qc->dcid = qc->dcid;
|
||||
cc_qc->scid = qc->scid;
|
||||
|
||||
cc_qc->li = qc->li;
|
||||
cc_qc->target = qc->target;
|
||||
cc_qc->cids = qc->cids;
|
||||
|
||||
cc_qc->idle_timer_task = qc->idle_timer_task;
|
||||
@ -771,6 +775,7 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
|
||||
struct list send_list = LIST_HEAD_INIT(send_list);
|
||||
struct quic_enc_level *qel;
|
||||
int st;
|
||||
int discard_hpktns = 0;
|
||||
struct tasklet *tl = (struct tasklet *)t;
|
||||
|
||||
TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
|
||||
@ -818,16 +823,30 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
|
||||
goto out;
|
||||
|
||||
st = qc->state;
|
||||
if (st >= QUIC_HS_ST_COMPLETE) {
|
||||
if (!(qc->flags & QUIC_FL_CONN_HPKTNS_DCD)) {
|
||||
/* Discard the Handshake packet number space. */
|
||||
TRACE_PROTO("discarding Handshake pktns", QUIC_EV_CONN_PHPKTS, qc);
|
||||
quic_pktns_discard(qc->hel->pktns, qc);
|
||||
qc_set_timer(qc);
|
||||
qc_el_rx_pkts_del(qc->hel);
|
||||
qc_release_pktns_frms(qc, qc->hel->pktns);
|
||||
}
|
||||
|
||||
if (qc_is_listener(qc)) {
|
||||
if (st >= QUIC_HS_ST_COMPLETE && !quic_tls_pktns_is_dcd(qc, qc->hpktns))
|
||||
discard_hpktns = 1;
|
||||
}
|
||||
else {
|
||||
if (st >= QUIC_HS_ST_CONFIRMED && !quic_tls_pktns_is_dcd(qc, qc->hpktns))
|
||||
discard_hpktns = 1;
|
||||
}
|
||||
|
||||
if (discard_hpktns) {
|
||||
/* Discard the Handshake packet number space. */
|
||||
TRACE_PROTO("discarding Handshake pktns", QUIC_EV_CONN_PHPKTS, qc);
|
||||
quic_pktns_discard(qc->hel->pktns, qc);
|
||||
qc_set_timer(qc);
|
||||
qc_el_rx_pkts_del(qc->hel);
|
||||
qc_release_pktns_frms(qc, qc->hel->pktns);
|
||||
if (!qc_is_listener(qc)) {
|
||||
/* I/O callback switch */
|
||||
qc->wait_event.tasklet->process = quic_conn_app_io_cb;
|
||||
}
|
||||
}
|
||||
|
||||
if (qc_is_listener(qc) && st >= QUIC_HS_ST_COMPLETE) {
|
||||
/* Note: if no token for address validation was received
|
||||
* for a 0RTT connection, some 0RTT packet could still be
|
||||
* waiting for HP removal AFTER the successful handshake completion.
|
||||
@ -885,6 +904,26 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* RFC 9001. 4.9.1. Discarding Initial Keys
|
||||
*
|
||||
* The successful use of Handshake packets indicates that no more Initial
|
||||
* packets need to be exchanged, as these keys can only be produced after
|
||||
* receiving all CRYPTO frames from Initial packets. Thus, a client MUST
|
||||
* discard Initial keys when it first sends a Handshake packet...
|
||||
*/
|
||||
|
||||
if (!qc_is_listener(qc) && !quic_tls_pktns_is_dcd(qc, qc->ipktns) &&
|
||||
qc->hpktns && qc->hpktns->tx.in_flight > 0) {
|
||||
/* Discard the Initial packet number space. */
|
||||
TRACE_PROTO("discarding Initial pktns", QUIC_EV_CONN_PRSHPKT, qc);
|
||||
quic_pktns_discard(qc->ipktns, qc);
|
||||
qc_set_timer(qc);
|
||||
qc_el_rx_pkts_del(qc->iel);
|
||||
qc_release_pktns_frms(qc, qc->ipktns);
|
||||
}
|
||||
|
||||
qc_txb_release(qc);
|
||||
|
||||
out:
|
||||
/* Release the Handshake encryption level and packet number space if
|
||||
* the Handshake is confirmed and if there is no need to send
|
||||
@ -1028,11 +1067,13 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
struct quic_connection_id *conn_id,
|
||||
struct sockaddr_storage *local_addr,
|
||||
struct sockaddr_storage *peer_addr,
|
||||
int server, int token, void *owner)
|
||||
int token, void *target,
|
||||
struct connection *conn)
|
||||
{
|
||||
struct quic_conn *qc = NULL;
|
||||
struct listener *l = server ? owner : NULL;
|
||||
struct proxy *prx = l ? l->bind_conf->frontend : NULL;
|
||||
struct listener *l = objt_listener(target);
|
||||
struct server *srv = objt_server(target);
|
||||
struct proxy *prx = l ? l->bind_conf->frontend : __objt_server(target)->proxy;
|
||||
struct quic_cc_algo *cc_algo = NULL;
|
||||
unsigned int next_actconn = 0, next_sslconn = 0, next_handshake = 0;
|
||||
|
||||
@ -1051,7 +1092,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (server) {
|
||||
if (l) {
|
||||
next_handshake = quic_increment_curr_handshake(l);
|
||||
if (!next_handshake) {
|
||||
TRACE_STATE("max handshake reached", QUIC_EV_CONN_INIT);
|
||||
@ -1065,6 +1106,14 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
goto err;
|
||||
}
|
||||
|
||||
qc->cids = pool_alloc(pool_head_quic_cids);
|
||||
if (!qc->cids) {
|
||||
TRACE_ERROR("Could not allocate a new CID tree", QUIC_EV_CONN_INIT, qc);
|
||||
goto err;
|
||||
}
|
||||
|
||||
qc->target = target;
|
||||
*qc->cids = EB_ROOT;
|
||||
/* Now that quic_conn instance is allocated, quic_conn_release() will
|
||||
* ensure global accounting is decremented.
|
||||
*/
|
||||
@ -1082,7 +1131,6 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
qc->streams_by_id = EB_ROOT_UNIQUE;
|
||||
|
||||
/* Required to call free_quic_conn_cids() from quic_conn_release() */
|
||||
qc->cids = NULL;
|
||||
qc->tx.cc_buf_area = NULL;
|
||||
qc_init_fd(qc);
|
||||
|
||||
@ -1096,7 +1144,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
qc->idle_timer_task = NULL;
|
||||
|
||||
qc->xprt_ctx = NULL;
|
||||
qc->conn = NULL;
|
||||
qc->conn = conn;
|
||||
qc->qcc = NULL;
|
||||
qc->app_ops = NULL;
|
||||
qc->path = NULL;
|
||||
@ -1117,16 +1165,12 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
/* Packet number spaces */
|
||||
qc->ipktns = qc->hpktns = qc->apktns = NULL;
|
||||
LIST_INIT(&qc->pktns_list);
|
||||
|
||||
/* Required to safely call quic_conn_prx_cntrs_update() from quic_conn_release(). */
|
||||
qc->prx_counters = NULL;
|
||||
qc->prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module);
|
||||
|
||||
/* QUIC Server (or listener). */
|
||||
if (server) {
|
||||
if (l) {
|
||||
cc_algo = l->bind_conf->quic_cc_algo;
|
||||
|
||||
qc->prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe,
|
||||
&quic_stats_module);
|
||||
qc->flags = QUIC_FL_CONN_LISTENER;
|
||||
/* Mark this connection as having not received any token when 0-RTT is enabled. */
|
||||
if (l->bind_conf->ssl_conf.early_data && !token)
|
||||
@ -1137,29 +1181,52 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
/* Copy the packet SCID to reuse it as DCID for sending */
|
||||
qc->dcid = *scid;
|
||||
qc->tx.buf = BUF_NULL;
|
||||
qc->li = l;
|
||||
conn_id->qc = qc;
|
||||
}
|
||||
/* QUIC Client (outgoing connection to servers) */
|
||||
else {
|
||||
struct quic_connection_id *conn_cid = NULL;
|
||||
|
||||
qc->flags = QUIC_FL_CONN_PEER_VALIDATED_ADDR;
|
||||
qc->state = QUIC_HS_ST_CLIENT_INITIAL;
|
||||
if (dcid->len)
|
||||
memcpy(qc->dcid.data, dcid->data, dcid->len);
|
||||
qc->dcid.len = dcid->len;
|
||||
qc->li = NULL;
|
||||
|
||||
memset(&qc->odcid, 0, sizeof qc->odcid);
|
||||
qc->odcid.len = 0;
|
||||
/* This is the original connection ID from the peer server
|
||||
* point of view.
|
||||
*/
|
||||
if (RAND_bytes(qc->dcid.data, sizeof(qc->dcid.data)) != 1)
|
||||
goto err;
|
||||
|
||||
qc->dcid.len = sizeof(qc->dcid.data);
|
||||
|
||||
conn_cid = new_quic_cid(qc->cids, qc, NULL, NULL);
|
||||
if (!conn_cid)
|
||||
goto err;
|
||||
|
||||
_quic_cid_insert(conn_cid);
|
||||
dcid = &qc->dcid;
|
||||
conn_id = conn_cid;
|
||||
|
||||
qc->tx.buf = BUF_NULL;
|
||||
qc->next_cid_seq_num = 1;
|
||||
conn->handle.qc = qc;
|
||||
}
|
||||
qc->mux_state = QC_MUX_NULL;
|
||||
qc->err = quic_err_transport(QC_ERR_NO_ERROR);
|
||||
|
||||
/* If connection is instantiated due to an INITIAL packet with an
|
||||
/* Listener only: if connection is instantiated due to an INITIAL packet with an
|
||||
* already checked token, consider the peer address as validated.
|
||||
*/
|
||||
if (token) {
|
||||
TRACE_STATE("validate peer address due to initial token",
|
||||
QUIC_EV_CONN_INIT, qc);
|
||||
qc->flags |= QUIC_FL_CONN_PEER_VALIDATED_ADDR;
|
||||
}
|
||||
else {
|
||||
HA_ATOMIC_INC(&qc->prx_counters->half_open_conn);
|
||||
if (l) {
|
||||
if (token_odcid->len) {
|
||||
TRACE_STATE("validate peer address due to initial token",
|
||||
QUIC_EV_CONN_INIT, qc);
|
||||
qc->flags |= QUIC_FL_CONN_PEER_VALIDATED_ADDR;
|
||||
}
|
||||
else {
|
||||
HA_ATOMIC_INC(&qc->prx_counters->half_open_conn);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now proceeds to allocation of qc members. */
|
||||
@ -1169,16 +1236,8 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
goto err;
|
||||
}
|
||||
|
||||
qc->cids = pool_alloc(pool_head_quic_cids);
|
||||
if (!qc->cids) {
|
||||
TRACE_ERROR("Could not allocate a new CID tree", QUIC_EV_CONN_INIT, qc);
|
||||
goto err;
|
||||
}
|
||||
*qc->cids = EB_ROOT;
|
||||
|
||||
conn_id->qc = qc;
|
||||
|
||||
if (HA_ATOMIC_LOAD(&l->rx.quic_mode) == QUIC_SOCK_MODE_CONN &&
|
||||
/* Listener only */
|
||||
if (l && HA_ATOMIC_LOAD(&l->rx.quic_mode) == QUIC_SOCK_MODE_CONN &&
|
||||
(quic_tune.options & QUIC_TUNE_SOCK_PER_CONN) &&
|
||||
is_addr(local_addr)) {
|
||||
TRACE_USER("Allocate a socket for QUIC connection", QUIC_EV_CONN_INIT, qc);
|
||||
@ -1217,6 +1276,10 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
qc->rx.buf = b_make(qc->rx.buf.area, QUIC_CONN_RX_BUFSZ, 0, 0);
|
||||
qc->rx.stream_max_uni = qc->rx.stream_max_bidi = 0;
|
||||
|
||||
qc->max_udp_payload = l ?
|
||||
l->bind_conf->quic_params.max_udp_payload_size :
|
||||
srv->quic_params.max_udp_payload_size;
|
||||
|
||||
qc->nb_pkt_for_cc = 1;
|
||||
qc->nb_pkt_since_cc = 0;
|
||||
|
||||
@ -1228,17 +1291,24 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
qc->max_ack_delay = 0;
|
||||
/* Only one path at this time (multipath not supported) */
|
||||
qc->path = &qc->paths[0];
|
||||
quic_cc_path_init(qc->path, ipv4, server ? l->bind_conf->max_cwnd : 0,
|
||||
quic_cc_path_init(qc->path, ipv4, l ? l->bind_conf->max_cwnd : 0,
|
||||
cc_algo ? cc_algo : default_quic_cc_algo, qc);
|
||||
|
||||
memcpy(&qc->local_addr, local_addr, sizeof(qc->local_addr));
|
||||
if (local_addr)
|
||||
memcpy(&qc->local_addr, local_addr, sizeof(qc->local_addr));
|
||||
else
|
||||
memset(&qc->local_addr, 0, sizeof(qc->local_addr));
|
||||
memcpy(&qc->peer_addr, peer_addr, sizeof qc->peer_addr);
|
||||
|
||||
if (server && !qc_lstnr_params_init(qc, &l->bind_conf->quic_params,
|
||||
conn_id->stateless_reset_token,
|
||||
dcid->data, dcid->len,
|
||||
qc->scid.data, qc->scid.len, token_odcid))
|
||||
goto err;
|
||||
if (l) {
|
||||
qc_lstnr_params_init(qc, &l->bind_conf->quic_params,
|
||||
conn_id->stateless_reset_token,
|
||||
qc->dcid.data, qc->dcid.len,
|
||||
qc->scid.data, qc->scid.len, token_odcid);
|
||||
}
|
||||
else {
|
||||
qc_srv_params_init(qc, &srv->quic_params, qc->scid.data, qc->scid.len);
|
||||
}
|
||||
|
||||
/* Initialize the idle timeout of the connection at the "max_idle_timeout"
|
||||
* value from local transport parameters.
|
||||
@ -1259,12 +1329,12 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
qc->wait_event.events = 0;
|
||||
qc->subs = NULL;
|
||||
|
||||
if (qc_alloc_ssl_sock_ctx(qc) ||
|
||||
if (qc_alloc_ssl_sock_ctx(qc, conn) ||
|
||||
!quic_conn_init_timer(qc) ||
|
||||
!quic_conn_init_idle_timer_task(qc, prx))
|
||||
goto err;
|
||||
|
||||
if (!qc_new_isecs(qc, &qc->iel->tls_ctx, qc->original_version, dcid->data, dcid->len, 1))
|
||||
if (!qc_new_isecs(qc, &qc->iel->tls_ctx, qc->original_version, dcid->data, dcid->len, !!l))
|
||||
goto err;
|
||||
|
||||
/* Counters initialization */
|
||||
@ -1278,6 +1348,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
return qc;
|
||||
|
||||
err:
|
||||
pool_free(pool_head_quic_connection_id, conn_id);
|
||||
quic_conn_release(qc);
|
||||
|
||||
/* Decrement global counters. Done only for errors happening before or
|
||||
@ -1314,7 +1385,7 @@ int qc_handle_conn_migration(struct quic_conn *qc,
|
||||
* used during the handshake, unless the endpoint has acted on a
|
||||
* preferred_address transport parameter from the peer.
|
||||
*/
|
||||
if (qc->li->bind_conf->quic_params.disable_active_migration) {
|
||||
if (__objt_listener(qc->target)->bind_conf->quic_params.disable_active_migration) {
|
||||
TRACE_ERROR("Active migration was disabled, datagram dropped", QUIC_EV_CONN_LPKT, qc);
|
||||
goto err;
|
||||
}
|
||||
@ -1444,8 +1515,8 @@ int quic_conn_release(struct quic_conn *qc)
|
||||
*/
|
||||
if (MT_LIST_INLIST(&qc->accept_list)) {
|
||||
MT_LIST_DELETE(&qc->accept_list);
|
||||
BUG_ON(qc->li->rx.quic_curr_accept == 0);
|
||||
HA_ATOMIC_DEC(&qc->li->rx.quic_curr_accept);
|
||||
BUG_ON(__objt_listener(qc->target)->rx.quic_curr_accept == 0);
|
||||
HA_ATOMIC_DEC(&__objt_listener(qc->target)->rx.quic_curr_accept);
|
||||
}
|
||||
|
||||
/* Substract last congestion window from global memory counter. */
|
||||
@ -1515,8 +1586,8 @@ int quic_conn_release(struct quic_conn *qc)
|
||||
/* Connection released before handshake completion. */
|
||||
if (unlikely(qc->state < QUIC_HS_ST_COMPLETE)) {
|
||||
if (qc_is_listener(qc)) {
|
||||
BUG_ON(qc->li->rx.quic_curr_handshake == 0);
|
||||
HA_ATOMIC_DEC(&qc->li->rx.quic_curr_handshake);
|
||||
BUG_ON(__objt_listener(qc->target)->rx.quic_curr_handshake == 0);
|
||||
HA_ATOMIC_DEC(&__objt_listener(qc->target)->rx.quic_curr_handshake);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1924,8 +1995,8 @@ void qc_bind_tid_commit(struct quic_conn *qc, struct listener *new_li)
|
||||
/* At this point no connection was accounted for yet on this
|
||||
* listener so it's OK to just swap the pointer.
|
||||
*/
|
||||
if (new_li && new_li != qc->li)
|
||||
qc->li = new_li;
|
||||
if (new_li && new_li != __objt_listener(qc->target))
|
||||
qc->target = &new_li->obj_type;
|
||||
|
||||
/* Rebind the connection FD. */
|
||||
if (qc_test_fd(qc)) {
|
||||
|
@ -58,7 +58,7 @@ static int qc_ssl_compat_add_tps_cb(SSL *ssl, unsigned int ext_type, unsigned in
|
||||
int quic_tls_compat_init(struct bind_conf *bind_conf, SSL_CTX *ctx)
|
||||
{
|
||||
/* Ignore non-QUIC connections */
|
||||
if (bind_conf->xprt != xprt_get(XPRT_QUIC))
|
||||
if (bind_conf && bind_conf->xprt != xprt_get(XPRT_QUIC))
|
||||
return 1;
|
||||
|
||||
/* This callback is already registered if the TLS keylog is activated for
|
||||
@ -150,22 +150,22 @@ void quic_tls_compat_keylog_callback(const SSL *ssl, const char *line)
|
||||
if (sizeof(QUIC_OPENSSL_COMPAT_CLIENT_HANDSHAKE) - 1 == n &&
|
||||
!strncmp(start, QUIC_OPENSSL_COMPAT_CLIENT_HANDSHAKE, n)) {
|
||||
level = ssl_encryption_handshake;
|
||||
write = 0;
|
||||
write = qc_is_listener(qc) ? 0 : 1;
|
||||
}
|
||||
else if (sizeof(QUIC_OPENSSL_COMPAT_SERVER_HANDSHAKE) - 1 == n &&
|
||||
!strncmp(start, QUIC_OPENSSL_COMPAT_SERVER_HANDSHAKE, n)) {
|
||||
level = ssl_encryption_handshake;
|
||||
write = 1;
|
||||
write = qc_is_listener(qc) ? 1 : 0;
|
||||
}
|
||||
else if (sizeof(QUIC_OPENSSL_COMPAT_CLIENT_APPLICATION) - 1 == n &&
|
||||
!strncmp(start, QUIC_OPENSSL_COMPAT_CLIENT_APPLICATION, n)) {
|
||||
level = ssl_encryption_application;
|
||||
write = 0;
|
||||
write = qc_is_listener(qc) ? 0 : 1;
|
||||
}
|
||||
else if (sizeof(QUIC_OPENSSL_COMPAT_SERVER_APPLICATION) - 1 == n &&
|
||||
!strncmp(start, QUIC_OPENSSL_COMPAT_SERVER_APPLICATION, n)) {
|
||||
level = ssl_encryption_application;
|
||||
write = 1;
|
||||
write = qc_is_listener(qc) ? 1 : 0;
|
||||
}
|
||||
else
|
||||
goto leave;
|
||||
|
@ -1819,8 +1819,8 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt,
|
||||
goto err;
|
||||
|
||||
qc = qc_new_conn(pkt->version, ipv4, &pkt->dcid, &pkt->scid, &token_odcid,
|
||||
conn_id, &dgram->daddr, &pkt->saddr, 1,
|
||||
!!pkt->token_len, l);
|
||||
conn_id, &dgram->daddr, &pkt->saddr,
|
||||
!!pkt->token_len, l, NULL);
|
||||
if (qc == NULL) {
|
||||
pool_free(pool_head_quic_connection_id, conn_id);
|
||||
goto err;
|
||||
@ -1877,25 +1877,25 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt,
|
||||
/* Parse a QUIC packet starting at <pos>. Data won't be read after <end> even
|
||||
* if the packet is incomplete. This function will populate fields of <pkt>
|
||||
* instance, most notably its length. <dgram> is the UDP datagram which
|
||||
* contains the parsed packet. <l> is the listener instance on which it was
|
||||
* received.
|
||||
* contains the parsed packet. <o> is the address object type address of the
|
||||
* object which receives this received packet. <qc> is the QUIC connection,
|
||||
* only valid for QUIC clients.
|
||||
*
|
||||
* Returns 0 on success else non-zero. Packet length is guaranteed to be set to
|
||||
* the real packet value or to cover all data between <pos> and <end> : this is
|
||||
* useful to reject a whole datagram.
|
||||
*/
|
||||
static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
|
||||
static int quic_rx_pkt_parse(struct quic_conn *qc, struct quic_rx_packet *pkt,
|
||||
unsigned char *pos, const unsigned char *end,
|
||||
struct quic_dgram *dgram, struct listener *l)
|
||||
struct quic_dgram *dgram, enum obj_type *o)
|
||||
{
|
||||
const unsigned char *beg = pos;
|
||||
struct proxy *prx;
|
||||
struct quic_counters *prx_counters;
|
||||
struct listener *l = objt_listener(o);
|
||||
|
||||
TRACE_ENTER(QUIC_EV_CONN_LPKT);
|
||||
|
||||
prx = l->bind_conf->frontend;
|
||||
prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module);
|
||||
prx_counters = qc_counters(o, &quic_stats_module);
|
||||
|
||||
if (end <= pos) {
|
||||
TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT);
|
||||
@ -1951,8 +1951,10 @@ static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
|
||||
goto drop;
|
||||
}
|
||||
|
||||
/* RFC9000 6. Version Negotiation */
|
||||
if (!pkt->version) {
|
||||
/* RFC9000 6. Version Negotiation. A Version Negotiation packet is
|
||||
* sent only by servers.
|
||||
*/
|
||||
if (l && !pkt->version) {
|
||||
/* unsupported version, send Negotiation packet */
|
||||
if (send_version_negotiation(l->rx.fd, &dgram->saddr, pkt)) {
|
||||
TRACE_ERROR("VN packet not sent", QUIC_EV_CONN_LPKT);
|
||||
@ -1976,6 +1978,13 @@ static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
|
||||
goto drop;
|
||||
}
|
||||
|
||||
if (!l && pkt->token_len) {
|
||||
/* A server must sent Initial packets with a null token length. */
|
||||
TRACE_PROTO("Packet dropped",
|
||||
QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
pkt->token = pos;
|
||||
pkt->token_len = token_len;
|
||||
pos += pkt->token_len;
|
||||
@ -2001,26 +2010,49 @@ static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
|
||||
pkt->pn_offset = pos - beg;
|
||||
pkt->len = pkt->pn_offset + len;
|
||||
|
||||
/* RFC 9000. Initial Datagram Size
|
||||
*
|
||||
* A server MUST discard an Initial packet that is carried in a UDP datagram
|
||||
* with a payload that is smaller than the smallest allowed maximum datagram
|
||||
* size of 1200 bytes.
|
||||
*/
|
||||
if (pkt->type == QUIC_PACKET_TYPE_INITIAL &&
|
||||
dgram->len < QUIC_INITIAL_PACKET_MINLEN) {
|
||||
TRACE_PROTO("RX too short datagram with an Initial packet", QUIC_EV_CONN_LPKT);
|
||||
HA_ATOMIC_INC(&prx_counters->too_short_initial_dgram);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
/* Interrupt parsing after packet length retrieval : this
|
||||
* ensures that only the packet is dropped but not the whole
|
||||
* datagram.
|
||||
*/
|
||||
if (pkt->type == QUIC_PACKET_TYPE_0RTT && !l->bind_conf->ssl_conf.early_data) {
|
||||
TRACE_PROTO("RX 0-RTT packet not supported", QUIC_EV_CONN_LPKT);
|
||||
goto drop;
|
||||
|
||||
if (pkt->type == QUIC_PACKET_TYPE_INITIAL) {
|
||||
if (l) {
|
||||
/* RFC 9000. Initial Datagram Size
|
||||
*
|
||||
* A server MUST discard an Initial packet that is carried in a UDP datagram
|
||||
* with a payload that is smaller than the smallest allowed maximum datagram
|
||||
* size of 1200 bytes.
|
||||
*/
|
||||
if (dgram->len < QUIC_INITIAL_PACKET_MINLEN) {
|
||||
TRACE_PROTO("RX too short datagram with an Initial packet", QUIC_EV_CONN_LPKT);
|
||||
HA_ATOMIC_INC(&prx_counters->too_short_initial_dgram);
|
||||
goto drop;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* TODO: This is not clear if a too short RX datagram which carries ack-eliciting
|
||||
* packets must be dropped by a client. If this is the case, this is not from
|
||||
* here, but after having parsed the datagram frames.
|
||||
*
|
||||
* RFC 9000. Initial Datagram Size
|
||||
*
|
||||
* Similarly, a server MUST expand the payload of all UDP datagrams carrying
|
||||
* ack-eliciting Initial packets to at least the smallest allowed maximum
|
||||
* datagram size of 1200 bytes.
|
||||
*/
|
||||
if (!(qc->flags & QUIC_FL_CONN_SCID_RECEIVED)) {
|
||||
qc->flags |= QUIC_FL_CONN_SCID_RECEIVED;
|
||||
memcpy(qc->dcid.data, pkt->scid.data, pkt->scid.len);
|
||||
qc->dcid.len = pkt->scid.len;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pkt->type == QUIC_PACKET_TYPE_0RTT) {
|
||||
/* O-RTT packet are not sent by servers. */
|
||||
if (!l || !l->bind_conf->ssl_conf.early_data) {
|
||||
TRACE_PROTO("RX 0-RTT packet not supported", QUIC_EV_CONN_LPKT);
|
||||
goto drop;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -2242,7 +2274,8 @@ static void qc_rx_pkt_handle(struct quic_conn *qc, struct quic_rx_packet *pkt,
|
||||
* this function.
|
||||
*
|
||||
* If datagram has been received on a quic-conn owned FD, <from_qc> must be set
|
||||
* to the connection instance. <li> is the attached listener. The caller is
|
||||
* to the connection instance. <o> is the object type address of the object
|
||||
* (listener or server) receiving the datagram. The caller is
|
||||
* responsible to ensure that the first packet is destined to this connection
|
||||
* by comparing CIDs.
|
||||
*
|
||||
@ -2254,7 +2287,7 @@ static void qc_rx_pkt_handle(struct quic_conn *qc, struct quic_rx_packet *pkt,
|
||||
* the datagram may not have been parsed.
|
||||
*/
|
||||
int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
|
||||
struct listener *li)
|
||||
enum obj_type *o)
|
||||
{
|
||||
struct quic_rx_packet *pkt;
|
||||
struct quic_conn *qc = NULL;
|
||||
@ -2292,7 +2325,7 @@ int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
|
||||
pkt->flags |= QUIC_FL_RX_PACKET_DGRAM_FIRST;
|
||||
|
||||
quic_rx_packet_refinc(pkt);
|
||||
if (quic_rx_pkt_parse(pkt, pos, end, dgram, li))
|
||||
if (quic_rx_pkt_parse(from_qc, pkt, pos, end, dgram, o))
|
||||
goto next;
|
||||
|
||||
/* Search quic-conn instance for first packet of the datagram.
|
||||
@ -2301,6 +2334,7 @@ int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
|
||||
*/
|
||||
if (!qc) {
|
||||
int new_tid = -1;
|
||||
struct listener *li = objt_listener(o);
|
||||
|
||||
qc = from_qc ? from_qc : quic_rx_pkt_retrieve_conn(pkt, dgram, li, &new_tid);
|
||||
/* qc is NULL if receiving a non Initial packet for an
|
||||
|
@ -87,13 +87,13 @@ int quic_sock_get_dst(struct connection *conn, struct sockaddr *addr, socklen_t
|
||||
memcpy(addr, &qc->peer_addr, len);
|
||||
} else {
|
||||
struct sockaddr_storage *from;
|
||||
struct listener *l = objt_listener(qc->target);
|
||||
|
||||
/* Return listener address if IP_PKTINFO or friends are not
|
||||
* supported by the socket.
|
||||
*/
|
||||
BUG_ON(!qc->li);
|
||||
from = is_addr(&qc->local_addr) ? &qc->local_addr :
|
||||
&qc->li->rx.addr;
|
||||
BUG_ON(!l);
|
||||
from = is_addr(&qc->local_addr) ? &qc->local_addr : &l->rx.addr;
|
||||
if (len > sizeof(*from))
|
||||
len = sizeof(*from);
|
||||
memcpy(addr, from, len);
|
||||
@ -815,36 +815,30 @@ int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz,
|
||||
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;
|
||||
struct listener *l = objt_listener(qc->target);
|
||||
|
||||
/* 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, DB_MUX_RX))
|
||||
break; /* TODO subscribe for memory again available. */
|
||||
|
||||
b_reset(&buf);
|
||||
BUG_ON(b_contig_space(&buf) < max_sz);
|
||||
BUG_ON(b_contig_space(&buf) < qc->max_udp_payload);
|
||||
|
||||
/* 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,
|
||||
ret = quic_recv(qc->fd, dgram_buf, qc->max_udp_payload,
|
||||
(struct sockaddr *)&saddr, sizeof(saddr),
|
||||
(struct sockaddr *)&daddr, sizeof(daddr),
|
||||
get_net_port(&qc->local_addr));
|
||||
@ -876,7 +870,7 @@ int qc_rcv_buf(struct quic_conn *qc)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!qc_check_dcid(qc, new_dgram->dcid, new_dgram->dcid_len)) {
|
||||
if (l && !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.
|
||||
*
|
||||
@ -934,7 +928,7 @@ int qc_rcv_buf(struct quic_conn *qc)
|
||||
continue;
|
||||
}
|
||||
|
||||
quic_dgram_parse(new_dgram, qc, qc->li);
|
||||
quic_dgram_parse(new_dgram, qc, qc->target);
|
||||
/* A datagram must always be consumed after quic_parse_dgram(). */
|
||||
BUG_ON(new_dgram->buf);
|
||||
} while (ret > 0);
|
||||
@ -956,11 +950,13 @@ int qc_rcv_buf(struct quic_conn *qc)
|
||||
*
|
||||
* Return the socket FD or a negative error code. On error, socket is marked as
|
||||
* uninitialized.
|
||||
* Note: This function must not be run for backends connection.
|
||||
*/
|
||||
void qc_alloc_fd(struct quic_conn *qc, const struct sockaddr_storage *src,
|
||||
const struct sockaddr_storage *dst)
|
||||
{
|
||||
struct bind_conf *bc = qc->li->bind_conf;
|
||||
struct listener *l = __objt_listener(qc->target);
|
||||
struct bind_conf *bc = l->bind_conf;
|
||||
struct proxy *p = bc->frontend;
|
||||
int fd = -1;
|
||||
int ret;
|
||||
@ -1013,7 +1009,7 @@ void qc_alloc_fd(struct quic_conn *qc, const struct sockaddr_storage *src,
|
||||
}
|
||||
|
||||
/* Fallback to listener socket for this receiver instance. */
|
||||
HA_ATOMIC_STORE(&qc->li->rx.quic_mode, QUIC_SOCK_MODE_LSTNR);
|
||||
HA_ATOMIC_STORE(&l->rx.quic_mode, QUIC_SOCK_MODE_LSTNR);
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
@ -1067,13 +1063,14 @@ struct quic_accept_queue *quic_accept_queues;
|
||||
void quic_accept_push_qc(struct quic_conn *qc)
|
||||
{
|
||||
struct quic_accept_queue *queue = &quic_accept_queues[tid];
|
||||
struct li_per_thread *lthr = &qc->li->per_thr[ti->ltid];
|
||||
struct listener *l = __objt_listener(qc->target);
|
||||
struct li_per_thread *lthr = &l->per_thr[ti->ltid];
|
||||
|
||||
/* A connection must only be accepted once per instance. */
|
||||
BUG_ON(qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED);
|
||||
|
||||
BUG_ON(MT_LIST_INLIST(&qc->accept_list));
|
||||
HA_ATOMIC_INC(&qc->li->rx.quic_curr_accept);
|
||||
HA_ATOMIC_INC(&l->rx.quic_curr_accept);
|
||||
|
||||
qc->flags |= QUIC_FL_CONN_ACCEPT_REGISTERED;
|
||||
/* 1. insert the listener in the accept queue
|
||||
|
165
src/quic_ssl.c
165
src/quic_ssl.c
@ -17,7 +17,7 @@ DECLARE_POOL(pool_head_quic_ssl_sock_ctx, "quic_ssl_sock_ctx", sizeof(struct ssl
|
||||
* be set to 1 for a QUIC server, 0 for a client.
|
||||
* Return 1 if succeeded, 0 if not.
|
||||
*/
|
||||
static int qc_ssl_set_quic_transport_params(struct quic_conn *qc,
|
||||
static int qc_ssl_set_quic_transport_params(SSL *ssl, struct quic_conn *qc,
|
||||
const struct quic_version *ver, int server)
|
||||
{
|
||||
int ret = 0;
|
||||
@ -40,7 +40,7 @@ static int qc_ssl_set_quic_transport_params(struct quic_conn *qc,
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (!SSL_set_quic_transport_params(qc->xprt_ctx->ssl, in, *enclen)) {
|
||||
if (!SSL_set_quic_transport_params(ssl, in, *enclen)) {
|
||||
TRACE_ERROR("SSL_set_quic_transport_params() failed", QUIC_EV_CONN_RWSEC);
|
||||
goto leave;
|
||||
}
|
||||
@ -274,16 +274,30 @@ write:
|
||||
|
||||
/* Set the transport parameters in the TLS stack. */
|
||||
if (level == ssl_encryption_handshake && qc_is_listener(qc) &&
|
||||
!qc_ssl_set_quic_transport_params(qc, ver, 1))
|
||||
!qc_ssl_set_quic_transport_params(qc->xprt_ctx->ssl, qc, ver, 1))
|
||||
goto leave;
|
||||
|
||||
keyupdate_init:
|
||||
/* Store the secret provided by the TLS stack, required for keyupdate. */
|
||||
if (level == ssl_encryption_application) {
|
||||
struct quic_tls_kp *prv_rx = &qc->ku.prv_rx;
|
||||
struct quic_tls_kp *nxt_rx = &qc->ku.nxt_rx;
|
||||
struct quic_tls_kp *nxt_tx = &qc->ku.nxt_tx;
|
||||
|
||||
#if !defined(USE_QUIC_OPENSSL_COMPAT) && !defined(HAVE_OPENSSL_QUIC)
|
||||
if (!qc_is_listener(qc)) {
|
||||
const unsigned char *tp;
|
||||
size_t tplen;
|
||||
|
||||
SSL_get_peer_quic_transport_params(ssl, &tp, &tplen);
|
||||
if (!tplen || !quic_transport_params_store(qc, 1,tp, tp + tplen)) {
|
||||
TRACE_ERROR("Could not parse remote transport paratemers",
|
||||
QUIC_EV_CONN_RWSEC, qc);
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Store the secret provided by the TLS stack, required for keyupdate. */
|
||||
if (rx) {
|
||||
if (!(rx->secret = pool_alloc(pool_head_quic_tls_secret))) {
|
||||
TRACE_ERROR("Could not allocate RX Application secrete keys", QUIC_EV_CONN_RWSEC, qc);
|
||||
@ -570,7 +584,7 @@ static int ha_quic_ossl_got_transport_params(SSL *ssl, const unsigned char *para
|
||||
}
|
||||
else {
|
||||
if (!quic_transport_params_store(qc, 0, params, params + params_len) ||
|
||||
!qc_ssl_set_quic_transport_params(qc, ver, 1))
|
||||
!qc_ssl_set_quic_transport_params(ssl, qc, ver, 1))
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -754,6 +768,44 @@ int ssl_quic_initial_ctx(struct bind_conf *bind_conf)
|
||||
return cfgerr;
|
||||
}
|
||||
|
||||
/* Allocate a TLS context for a QUIC server.
|
||||
* Return this context if succeeded, NULL if failed.
|
||||
*/
|
||||
SSL_CTX *ssl_quic_srv_new_ssl_ctx(void)
|
||||
{
|
||||
SSL_CTX *ctx = NULL;
|
||||
/* XXX TODO: check this: XXX */
|
||||
long options =
|
||||
(SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
|
||||
SSL_OP_SINGLE_ECDH_USE |
|
||||
SSL_OP_CIPHER_SERVER_PREFERENCE;
|
||||
|
||||
TRACE_ENTER(QUIC_EV_CONN_NEW);
|
||||
|
||||
ctx = SSL_CTX_new(TLS_client_method());
|
||||
if (!ctx) {
|
||||
TRACE_ERROR("Could not allocate a new TLS context", QUIC_EV_CONN_NEW);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
SSL_CTX_set_options(ctx, options);
|
||||
SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION);
|
||||
SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION);
|
||||
#ifdef USE_QUIC_OPENSSL_COMPAT
|
||||
if (!quic_tls_compat_init(NULL, ctx))
|
||||
goto err;
|
||||
#endif
|
||||
|
||||
leave:
|
||||
TRACE_LEAVE(QUIC_EV_CONN_NEW);
|
||||
return ctx;
|
||||
err:
|
||||
SSL_CTX_free(ctx);
|
||||
ctx = NULL;
|
||||
TRACE_DEVEL("leaving on error", QUIC_EV_CONN_NEW);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* This function gives the detail of the SSL error. It is used only
|
||||
* if the debug mode and the verbose mode are activated. It dump all
|
||||
* the SSL error until the stack was empty.
|
||||
@ -885,28 +937,54 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
|
||||
* provided by the stack. This happens after having received the peer
|
||||
* handshake level CRYPTO data which are validated by the TLS stack.
|
||||
*/
|
||||
if (qc->li->bind_conf->ssl_conf.early_data &&
|
||||
(!qc->ael || !qc->ael->tls_ctx.rx.secret)) {
|
||||
TRACE_PROTO("SSL handshake in progress",
|
||||
QUIC_EV_CONN_IO_CB, qc, &state, &ssl_err);
|
||||
goto out;
|
||||
}
|
||||
else {
|
||||
TRACE_PROTO("SSL handshake OK", QUIC_EV_CONN_IO_CB, qc, &state);
|
||||
if (qc_is_listener(qc)) {
|
||||
if (__objt_listener(qc->target)->bind_conf->ssl_conf.early_data &&
|
||||
(!qc->ael || !qc->ael->tls_ctx.rx.secret)) {
|
||||
TRACE_PROTO("SSL handshake in progress",
|
||||
QUIC_EV_CONN_IO_CB, qc, &state, &ssl_err);
|
||||
goto out;
|
||||
}
|
||||
else {
|
||||
TRACE_PROTO("SSL handshake OK", QUIC_EV_CONN_IO_CB, qc, &state);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Check the alpn could be negotiated */
|
||||
if (!qc->app_ops) {
|
||||
TRACE_ERROR("No negotiated ALPN", QUIC_EV_CONN_IO_CB, qc, &state);
|
||||
quic_set_tls_alert(qc, SSL_AD_NO_APPLICATION_PROTOCOL);
|
||||
goto leave;
|
||||
if (qc_is_listener(qc)) {
|
||||
if (!qc->app_ops) {
|
||||
TRACE_ERROR("No negotiated ALPN", QUIC_EV_CONN_IO_CB, qc, &state);
|
||||
quic_set_tls_alert(qc, SSL_AD_NO_APPLICATION_PROTOCOL);
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const unsigned char *alpn;
|
||||
size_t alpn_len;
|
||||
struct server *s = objt_server(ctx->conn->target);
|
||||
|
||||
ctx->conn->flags &= ~(CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN);
|
||||
if (!ssl_sock_get_alpn(ctx->conn, ctx, (const char **)&alpn, (int *)&alpn_len) ||
|
||||
!quic_set_app_ops(qc, alpn, alpn_len)) {
|
||||
TRACE_ERROR("No negotiated ALPN", QUIC_EV_CONN_IO_CB, qc, &state);
|
||||
quic_set_tls_alert(qc, SSL_AD_NO_APPLICATION_PROTOCOL);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
s->mux_proto = get_mux_proto(ist("quic"));
|
||||
if (conn_create_mux(ctx->conn, NULL) < 0) {
|
||||
TRACE_ERROR("mux creation failed", QUIC_EV_CONN_IO_CB, qc, &state);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
qc->mux_state = QC_MUX_READY;
|
||||
}
|
||||
|
||||
/* I/O callback switch */
|
||||
qc->wait_event.tasklet->process = quic_conn_app_io_cb;
|
||||
qc->flags |= QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS;
|
||||
if (qc_is_listener(ctx->qc)) {
|
||||
qc->flags |= QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS;
|
||||
struct listener *l = __objt_listener(qc->target);
|
||||
/* I/O callback switch */
|
||||
qc->wait_event.tasklet->process = quic_conn_app_io_cb;
|
||||
qc->state = QUIC_HS_ST_CONFIRMED;
|
||||
|
||||
if (!(qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED)) {
|
||||
@ -920,8 +998,8 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
|
||||
tasklet_wakeup(qc->wait_event.tasklet);
|
||||
}
|
||||
|
||||
BUG_ON(qc->li->rx.quic_curr_handshake == 0);
|
||||
HA_ATOMIC_DEC(&qc->li->rx.quic_curr_handshake);
|
||||
BUG_ON(l->rx.quic_curr_handshake == 0);
|
||||
HA_ATOMIC_DEC(&l->rx.quic_curr_handshake);
|
||||
}
|
||||
else {
|
||||
qc->state = QUIC_HS_ST_COMPLETE;
|
||||
@ -1041,7 +1119,8 @@ int quic_ssl_set_tls_cbs(SSL *ssl)
|
||||
* Return 0 if succeeded, -1 if not. If failed, sets the ->err_code member of <qc->conn> to
|
||||
* CO_ER_SSL_NO_MEM.
|
||||
*/
|
||||
static int qc_ssl_sess_init(struct quic_conn *qc, SSL_CTX *ssl_ctx, SSL **ssl)
|
||||
static int qc_ssl_sess_init(struct quic_conn *qc, SSL_CTX *ssl_ctx, SSL **ssl,
|
||||
struct connection *conn, int server)
|
||||
{
|
||||
int retry, ret = -1;
|
||||
|
||||
@ -1059,6 +1138,7 @@ static int qc_ssl_sess_init(struct quic_conn *qc, SSL_CTX *ssl_ctx, SSL **ssl)
|
||||
}
|
||||
|
||||
if (!SSL_set_ex_data(*ssl, ssl_qc_app_data_index, qc) ||
|
||||
(!server && !SSL_set_ex_data(*ssl, ssl_app_data_index, conn)) ||
|
||||
!quic_ssl_set_tls_cbs(*ssl)) {
|
||||
SSL_free(*ssl);
|
||||
*ssl = NULL;
|
||||
@ -1121,10 +1201,9 @@ static int qc_set_quic_early_data_enabled(struct quic_conn *qc, SSL *ssl)
|
||||
*
|
||||
* Returns 0 on success else non-zero.
|
||||
*/
|
||||
int qc_alloc_ssl_sock_ctx(struct quic_conn *qc)
|
||||
int qc_alloc_ssl_sock_ctx(struct quic_conn *qc, struct connection *conn)
|
||||
{
|
||||
int ret = 0;
|
||||
struct bind_conf *bc = qc->li->bind_conf;
|
||||
struct ssl_sock_ctx *ctx = NULL;
|
||||
|
||||
TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
|
||||
@ -1135,7 +1214,7 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ctx->conn = NULL;
|
||||
ctx->conn = conn;
|
||||
ctx->bio = NULL;
|
||||
ctx->xprt = NULL;
|
||||
ctx->xprt_ctx = NULL;
|
||||
@ -1148,7 +1227,9 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc)
|
||||
ctx->qc = qc;
|
||||
|
||||
if (qc_is_listener(qc)) {
|
||||
if (qc_ssl_sess_init(qc, bc->initial_ctx, &ctx->ssl) == -1)
|
||||
struct bind_conf *bc = __objt_listener(qc->target)->bind_conf;
|
||||
|
||||
if (qc_ssl_sess_init(qc, bc->initial_ctx, &ctx->ssl, NULL, 1) == -1)
|
||||
goto err;
|
||||
#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L) && defined(HAVE_SSL_0RTT_QUIC)
|
||||
/* Enabling 0-RTT */
|
||||
@ -1158,6 +1239,36 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc)
|
||||
|
||||
SSL_set_accept_state(ctx->ssl);
|
||||
}
|
||||
else {
|
||||
int ssl_err;
|
||||
struct server *srv = __objt_server(ctx->conn->target);
|
||||
|
||||
if (qc_ssl_sess_init(qc, srv->ssl_ctx.ctx, &ctx->ssl, conn, 0) == -1)
|
||||
goto err;
|
||||
|
||||
if (!qc_ssl_set_quic_transport_params(ctx->ssl, qc, quic_version_1, 0))
|
||||
goto err;
|
||||
|
||||
SSL_set_connect_state(ctx->ssl);
|
||||
ssl_err = SSL_do_handshake(ctx->ssl);
|
||||
TRACE_PROTO("SSL_do_handshake() called", QUIC_EV_CONN_NEW, qc, &ssl_err);
|
||||
if (ssl_err != 1) {
|
||||
ssl_err = SSL_get_error(ctx->ssl, ssl_err);
|
||||
if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE) {
|
||||
TRACE_PROTO("SSL handshake in progress", QUIC_EV_CONN_NEW, qc, &ssl_err);
|
||||
}
|
||||
else {
|
||||
TRACE_ERROR("SSL handshake error", QUIC_EV_CONN_NEW, qc, &ssl_err);
|
||||
HA_ATOMIC_INC(&qc->prx_counters->hdshk_fail);
|
||||
qc_ssl_dump_errors(ctx->conn);
|
||||
ERR_clear_error();
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wakeup the handshake I/O handler tasklet asap to send data */
|
||||
tasklet_wakeup(qc->wait_event.tasklet);
|
||||
}
|
||||
|
||||
ctx->xprt = xprt_get(XPRT_QUIC);
|
||||
|
||||
|
@ -213,9 +213,18 @@ static int quic_transport_param_dec_version_info(struct tp_version_information *
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (server)
|
||||
/* TODO: not supported */
|
||||
if (server) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < quic_versions_nb; i++) {
|
||||
if (tp->chosen == quic_versions[i].num) {
|
||||
tp->negotiated_version = &quic_versions[i];
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (ver = others; ver < end; ver += 4) {
|
||||
if (!tp->negotiated_version) {
|
||||
@ -513,8 +522,21 @@ static int quic_transport_param_enc_version_info(unsigned char **buf,
|
||||
memcpy(*buf, &ver, sizeof ver);
|
||||
*buf += sizeof ver;
|
||||
/* For servers: all supported version, chosen included */
|
||||
for (i = 0; i < quic_versions_nb; i++) {
|
||||
ver = htonl(quic_versions[i].num);
|
||||
if (server) {
|
||||
for (i = 0; i < quic_versions_nb; i++) {
|
||||
ver = htonl(quic_versions[i].num);
|
||||
memcpy(*buf, &ver, sizeof ver);
|
||||
*buf += sizeof ver;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ver = htonl(quic_version_1->num);
|
||||
memcpy(*buf, &ver, sizeof ver);
|
||||
*buf += sizeof ver;
|
||||
ver = htonl(quic_version_2->num);
|
||||
memcpy(*buf, &ver, sizeof ver);
|
||||
*buf += sizeof ver;
|
||||
ver = htonl(quic_version_draft_29->num);
|
||||
memcpy(*buf, &ver, sizeof ver);
|
||||
*buf += sizeof ver;
|
||||
}
|
||||
@ -806,3 +828,21 @@ int qc_lstnr_params_init(struct quic_conn *qc,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* QUIC client (or haproxy server) only function.
|
||||
* Initialize the local transport parameters <rx_params> from <srv_params>
|
||||
* coming from configuration and source connection ID).
|
||||
* Never fails.
|
||||
*/
|
||||
void qc_srv_params_init(struct quic_conn *qc,
|
||||
const struct quic_transport_params *srv_params,
|
||||
const unsigned char *scid, size_t scidlen)
|
||||
{
|
||||
struct quic_transport_params *rx_params = &qc->rx.params;
|
||||
|
||||
/* Copy the transport parameters. */
|
||||
*rx_params = *srv_params;
|
||||
/* Copy the initial source connection ID. */
|
||||
memcpy(rx_params->initial_source_connection_id.data, scid, scidlen);
|
||||
rx_params->initial_source_connection_id.len = scidlen;
|
||||
TRACE_PROTO("\nRX(local) transp. params.", QUIC_EV_TRANSP_PARAMS, qc, rx_params);
|
||||
}
|
||||
|
@ -117,6 +117,13 @@ static void quic_trace(enum trace_level level, uint64_t mask, const struct trace
|
||||
|
||||
chunk_appendf(&trace_buf, " : qc@%p idle_timer_task@%p flags=0x%x",
|
||||
qc, qc->idle_timer_task, qc->flags);
|
||||
if (mask & QUIC_EV_CONN_NEW) {
|
||||
const int *ssl_err = a2;
|
||||
|
||||
if (ssl_err)
|
||||
chunk_appendf(&trace_buf, " ssl_err=%d", *ssl_err);
|
||||
}
|
||||
|
||||
if (mask & QUIC_EV_CONN_INIT) {
|
||||
chunk_appendf(&trace_buf, "\n odcid");
|
||||
quic_cid_dump(&trace_buf, &qc->odcid);
|
||||
|
@ -288,8 +288,10 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx)
|
||||
int ret = 0;
|
||||
struct quic_conn *qc;
|
||||
char skip_sendto = 0;
|
||||
struct listener *l;
|
||||
|
||||
qc = ctx->qc;
|
||||
l = objt_listener(qc->target);
|
||||
TRACE_ENTER(QUIC_EV_CONN_SPPKTS, qc);
|
||||
while (b_contig_data(buf, 0)) {
|
||||
unsigned char *pos;
|
||||
@ -305,7 +307,11 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx)
|
||||
|
||||
/* If datagram bigger than MTU, several ones were encoded for GSO usage. */
|
||||
if (dglen > qc->path->mtu) {
|
||||
if (likely(!(HA_ATOMIC_LOAD(&qc->li->flags) & LI_F_UDP_GSO_NOTSUPP))) {
|
||||
/* TODO: note that at this time for connection to backends this
|
||||
* part is not run because no more than an MTU has been prepared for
|
||||
* such connections (dglen <= qc->path->mtu). So, here l is not NULL.
|
||||
*/
|
||||
if (likely(!(HA_ATOMIC_LOAD(&l->flags) & LI_F_UDP_GSO_NOTSUPP))) {
|
||||
TRACE_PROTO("send multiple datagrams with GSO", QUIC_EV_CONN_SPPKTS, qc);
|
||||
gso = qc->path->mtu;
|
||||
}
|
||||
@ -327,11 +333,15 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx)
|
||||
int ret = qc_snd_buf(qc, &tmpbuf, tmpbuf.data, 0, gso);
|
||||
if (ret < 0) {
|
||||
if (gso && ret == -EIO) {
|
||||
/* TODO: note that at this time for connection to backends this
|
||||
* part is not run because no more than an MTU has been
|
||||
* prepared for such connections (l is not NULL).
|
||||
*/
|
||||
/* Disable permanently UDP GSO for this listener.
|
||||
* Retry standard emission.
|
||||
*/
|
||||
TRACE_ERROR("mark listener UDP GSO as unsupported", QUIC_EV_CONN_SPPKTS, qc, first_pkt);
|
||||
HA_ATOMIC_OR(&qc->li->flags, LI_F_UDP_GSO_NOTSUPP);
|
||||
HA_ATOMIC_OR(&l->flags, LI_F_UDP_GSO_NOTSUPP);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -576,6 +586,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
|
||||
int dgram_cnt = 0;
|
||||
/* Restrict GSO emission to comply with sendmsg limitation. See QUIC_MAX_GSO_DGRAMS for more details. */
|
||||
uchar gso_dgram_cnt = 0;
|
||||
struct listener *l = objt_listener(qc->target);
|
||||
|
||||
TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
|
||||
/* Currently qc_prep_pkts() does not handle buffer wrapping so the
|
||||
@ -765,11 +776,13 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
|
||||
prv_pkt = cur_pkt;
|
||||
}
|
||||
else if (!(quic_tune.options & QUIC_TUNE_NO_UDP_GSO) &&
|
||||
!(HA_ATOMIC_LOAD(&qc->li->flags) & LI_F_UDP_GSO_NOTSUPP) &&
|
||||
dglen == qc->path->mtu &&
|
||||
(char *)end < b_wrap(buf) &&
|
||||
++gso_dgram_cnt < QUIC_MAX_GSO_DGRAMS) {
|
||||
|
||||
++gso_dgram_cnt < QUIC_MAX_GSO_DGRAMS &&
|
||||
l && !(HA_ATOMIC_LOAD(&l->flags) & LI_F_UDP_GSO_NOTSUPP)) {
|
||||
/* TODO: note that for backends GSO is not used. No more than
|
||||
* an MTU is prepared.
|
||||
*/
|
||||
/* A datagram covering the full MTU has been
|
||||
* built, use GSO to built next entry. Do not
|
||||
* reserve extra space for datagram header.
|
||||
|
17
src/server.c
17
src/server.c
@ -37,6 +37,7 @@
|
||||
#include <haproxy/protocol.h>
|
||||
#include <haproxy/proxy.h>
|
||||
#include <haproxy/queue.h>
|
||||
#include <haproxy/quic_tp.h>
|
||||
#include <haproxy/resolvers.h>
|
||||
#include <haproxy/sample.h>
|
||||
#include <haproxy/sc_strm.h>
|
||||
@ -3589,6 +3590,7 @@ static int _srv_parse_init(struct server **srv, char **args, int *cur_arg,
|
||||
(parse_flags & SRV_PARSE_INITIAL_RESOLVE ? PA_O_RESOLVE : 0) | PA_O_PORT_OK |
|
||||
(parse_flags & SRV_PARSE_IN_PEER_SECTION ? PA_O_PORT_MAND : PA_O_PORT_OFS) |
|
||||
PA_O_STREAM | PA_O_DGRAM | PA_O_XPRT);
|
||||
|
||||
if (!sk) {
|
||||
ha_alert("%s\n", errmsg);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
@ -3596,6 +3598,21 @@ static int _srv_parse_init(struct server **srv, char **args, int *cur_arg,
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef USE_QUIC
|
||||
if (srv_is_quic(newsrv)) {
|
||||
if (!experimental_directives_allowed) {
|
||||
ha_alert("QUIC is experimental for server '%s',"
|
||||
" must be allowed via a global 'expose-experimental-directives'\n",
|
||||
newsrv->id);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
newsrv->xprt = xprt_get(XPRT_QUIC);
|
||||
quic_transport_params_init(&newsrv->quic_params, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!port1 || !port2) {
|
||||
if (sk->ss_family != AF_CUST_RHTTP_SRV) {
|
||||
/* no port specified, +offset, -offset */
|
||||
|
@ -265,7 +265,8 @@ static int sock_handle_system_err(struct connection *conn, struct proxy *be)
|
||||
* upper level is set as SF_ERR_NONE; -1 on failure, stream_err is set to
|
||||
* appropriate value.
|
||||
*/
|
||||
int sock_create_server_socket(struct connection *conn, struct proxy *be, int *stream_err)
|
||||
int sock_create_server_socket(struct connection *conn, struct proxy *be,
|
||||
enum proto_type proto_type, int sock_type, int *stream_err)
|
||||
{
|
||||
const struct netns_entry *ns = NULL;
|
||||
const struct protocol *proto;
|
||||
@ -279,9 +280,9 @@ int sock_create_server_socket(struct connection *conn, struct proxy *be, int *st
|
||||
ns = __objt_server(conn->target)->netns;
|
||||
}
|
||||
#endif
|
||||
proto = protocol_lookup(conn->dst->ss_family, PROTO_TYPE_STREAM, conn->ctrl->sock_prot == IPPROTO_MPTCP);
|
||||
proto = protocol_lookup(conn->dst->ss_family, proto_type , conn->ctrl->sock_prot == IPPROTO_MPTCP);
|
||||
BUG_ON(!proto);
|
||||
sock_fd = my_socketat(ns, proto->fam->sock_domain, SOCK_STREAM, proto->sock_prot);
|
||||
sock_fd = my_socketat(ns, proto->fam->sock_domain, sock_type, proto->sock_prot);
|
||||
|
||||
/* at first, handle common to all proto families system limits and permission related errors */
|
||||
if (sock_fd == -1) {
|
||||
|
@ -2601,8 +2601,9 @@ int ckch_inst_rebuild(struct ckch_store *ckch_store, struct ckch_inst *ckchi,
|
||||
fcount = ckchi->crtlist_entry->fcount;
|
||||
}
|
||||
|
||||
if (ckchi->is_server_instance)
|
||||
errcode |= ckch_inst_new_load_srv_store(ckch_store->path, ckch_store, new_inst, err);
|
||||
if (ckchi->is_server_instance) {
|
||||
errcode |= ckch_inst_new_load_srv_store(ckch_store->path, ckch_store, new_inst, err, srv_is_quic(ckchi->server));
|
||||
}
|
||||
else
|
||||
errcode |= ckch_inst_new_load_store(ckch_store->path, ckch_store, ckchi->bind_conf, ckchi->ssl_conf, sni_filter, fcount, ckchi->is_default, new_inst, err);
|
||||
|
||||
|
@ -177,7 +177,7 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *arg)
|
||||
s = __objt_listener(conn->target)->bind_conf;
|
||||
#ifdef USE_QUIC
|
||||
else if (qc)
|
||||
s = qc->li->bind_conf;
|
||||
s = __objt_listener(qc->target)->bind_conf;
|
||||
#endif /* USE_QUIC */
|
||||
|
||||
if (!s) {
|
||||
|
@ -127,7 +127,7 @@ int ssl_sock_ocsp_stapling_cbk(SSL *ssl, void *arg)
|
||||
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
||||
|
||||
/* null if not a listener */
|
||||
li = qc->li;
|
||||
li = objt_listener(qc->target);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -65,6 +65,7 @@
|
||||
#include <haproxy/proxy.h>
|
||||
#include <haproxy/quic_conn.h>
|
||||
#include <haproxy/quic_openssl_compat.h>
|
||||
#include <haproxy/quic_ssl.h>
|
||||
#include <haproxy/quic_tp.h>
|
||||
#include <haproxy/sample.h>
|
||||
#include <haproxy/sc_strm.h>
|
||||
@ -925,7 +926,7 @@ static int ssl_tlsext_ticket_key_cb(SSL *s, unsigned char key_name[16], unsigned
|
||||
ref = __objt_listener(conn->target)->bind_conf->keys_ref;
|
||||
#ifdef USE_QUIC
|
||||
else if (qc)
|
||||
ref = qc->li->bind_conf->keys_ref;
|
||||
ref = __objt_listener(qc->target)->bind_conf->keys_ref;
|
||||
#endif
|
||||
|
||||
if (!ref) {
|
||||
@ -1481,7 +1482,7 @@ int ssl_sock_bind_verifycbk(int ok, X509_STORE_CTX *x_store)
|
||||
else {
|
||||
qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
||||
BUG_ON(!qc); /* Must never happen */
|
||||
bind_conf = qc->li->bind_conf;
|
||||
bind_conf = __objt_listener(qc->target)->bind_conf;
|
||||
ctx = qc->xprt_ctx;
|
||||
}
|
||||
#endif
|
||||
@ -3039,6 +3040,20 @@ error:
|
||||
return errcode;
|
||||
}
|
||||
|
||||
#ifdef USE_QUIC
|
||||
static inline SSL_CTX *ssl_sock_new_ssl_ctx(int is_quic)
|
||||
{
|
||||
if (is_quic)
|
||||
return ssl_quic_srv_new_ssl_ctx();
|
||||
else
|
||||
return SSL_CTX_new(SSLv23_client_method());
|
||||
}
|
||||
#else
|
||||
static inline SSL_CTX *ssl_sock_new_ssl_ctx(int is_quic)
|
||||
{
|
||||
return SSL_CTX_new(SSLv23_client_method());
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This function allocate a ckch_inst that will be used on the backend side
|
||||
@ -3050,7 +3065,7 @@ error:
|
||||
* ERR_WARN if a warning is available into err
|
||||
*/
|
||||
int ckch_inst_new_load_srv_store(const char *path, struct ckch_store *ckchs,
|
||||
struct ckch_inst **ckchi, char **err)
|
||||
struct ckch_inst **ckchi, char **err, int is_quic)
|
||||
{
|
||||
SSL_CTX *ctx;
|
||||
struct ckch_data *data;
|
||||
@ -3064,7 +3079,7 @@ int ckch_inst_new_load_srv_store(const char *path, struct ckch_store *ckchs,
|
||||
|
||||
data = ckchs->data;
|
||||
|
||||
ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
ctx = ssl_sock_new_ssl_ctx(is_quic);
|
||||
if (!ctx) {
|
||||
memprintf(err, "%sunable to allocate SSL context for cert '%s'.\n",
|
||||
err && *err ? *err : "", path);
|
||||
@ -3135,7 +3150,8 @@ static int ssl_sock_load_srv_ckchs(const char *path, struct ckch_store *ckchs,
|
||||
int errcode = 0;
|
||||
|
||||
/* we found the ckchs in the tree, we can use it directly */
|
||||
errcode |= ckch_inst_new_load_srv_store(path, ckchs, ckch_inst, err);
|
||||
errcode |= ckch_inst_new_load_srv_store(path, ckchs, ckch_inst, err,
|
||||
srv_is_quic(server));
|
||||
|
||||
if (errcode & ERR_CODE)
|
||||
return errcode;
|
||||
@ -4398,7 +4414,9 @@ int ssl_sock_prepare_srv_ctx(struct server *srv)
|
||||
return cfgerr;
|
||||
}
|
||||
}
|
||||
if (srv->use_ssl == 1)
|
||||
|
||||
/* The QUIC server xprt has already been set. */
|
||||
if (srv->use_ssl == 1 && !srv_is_quic(srv))
|
||||
srv->xprt = &ssl_sock;
|
||||
|
||||
if (srv->ssl_ctx.client_crt) {
|
||||
@ -4425,7 +4443,7 @@ int ssl_sock_prepare_srv_ctx(struct server *srv)
|
||||
/* The context will be uninitialized if there wasn't any "cert" option
|
||||
* in the server line. */
|
||||
if (!ctx) {
|
||||
ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
ctx = ssl_sock_new_ssl_ctx(srv_is_quic(srv));
|
||||
if (!ctx) {
|
||||
ha_alert("unable to allocate ssl context.\n");
|
||||
cfgerr++;
|
||||
|
@ -111,10 +111,25 @@ static int quic_conn_unsubscribe(struct connection *conn, void *xprt_ctx, int ev
|
||||
*/
|
||||
static int qc_conn_init(struct connection *conn, void **xprt_ctx)
|
||||
{
|
||||
struct quic_conn *qc = conn->handle.qc;
|
||||
int ret = -1;
|
||||
struct quic_conn *qc = NULL;
|
||||
|
||||
TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
|
||||
|
||||
if (objt_listener(conn->target)) {
|
||||
qc = conn->handle.qc;
|
||||
}
|
||||
else {
|
||||
int ipv4 = conn->dst->ss_family == AF_INET;
|
||||
struct server *srv = objt_server(conn->target);
|
||||
qc = qc_new_conn(quic_version_1, ipv4, NULL, NULL, NULL,
|
||||
NULL, NULL, &srv->addr, 0, srv, conn);
|
||||
}
|
||||
|
||||
if (!qc)
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
/* Ensure thread connection migration is finalized ASAP. */
|
||||
if (qc->flags & QUIC_FL_CONN_TID_REBIND)
|
||||
qc_finalize_tid_rebind(qc);
|
||||
@ -128,7 +143,7 @@ static int qc_conn_init(struct connection *conn, void **xprt_ctx)
|
||||
out:
|
||||
TRACE_LEAVE(QUIC_EV_CONN_NEW, qc);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Start the QUIC transport layer */
|
||||
@ -140,8 +155,13 @@ static int qc_xprt_start(struct connection *conn, void *ctx)
|
||||
qc = conn->handle.qc;
|
||||
TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
|
||||
|
||||
/* mux-quic can now be considered ready. */
|
||||
qc->mux_state = QC_MUX_READY;
|
||||
if (objt_listener(conn->target)) {
|
||||
/* mux-quic can now be considered ready. */
|
||||
qc->mux_state = QC_MUX_READY;
|
||||
}
|
||||
else {
|
||||
conn->flags |= CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN;
|
||||
}
|
||||
|
||||
/* Schedule quic-conn to ensure post handshake frames are emitted. This
|
||||
* is not done for 0-RTT as xprt->start happens before handshake
|
||||
@ -178,6 +198,8 @@ static struct xprt_ops ssl_quic = {
|
||||
.start = qc_xprt_start,
|
||||
.prepare_bind_conf = ssl_sock_prepare_bind_conf,
|
||||
.destroy_bind_conf = ssl_sock_destroy_bind_conf,
|
||||
.prepare_srv = ssl_sock_prepare_srv_ctx,
|
||||
.destroy_srv = ssl_sock_free_srv_ctx,
|
||||
.get_alpn = ssl_sock_get_alpn,
|
||||
.get_ssl_sock_ctx = qc_get_ssl_sock_ctx,
|
||||
.dump_info = qc_xprt_dump_info,
|
||||
|
Loading…
x
Reference in New Issue
Block a user