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 qcs {
|
||||||
struct qcc *qcc;
|
struct qcc *qcc;
|
||||||
struct sedesc *sd;
|
struct sedesc *sd;
|
||||||
|
struct session *sess;
|
||||||
uint32_t flags; /* QC_SF_* */
|
uint32_t flags; /* QC_SF_* */
|
||||||
enum qcs_state st; /* QC_SS_* state */
|
enum qcs_state st; /* QC_SS_* state */
|
||||||
void *ctx; /* app-ops context */
|
void *ctx; /* app-ops context */
|
||||||
|
@ -228,6 +228,9 @@ struct quic_version {
|
|||||||
extern const struct quic_version quic_versions[];
|
extern const struct quic_version quic_versions[];
|
||||||
extern const size_t quic_versions_nb;
|
extern const size_t quic_versions_nb;
|
||||||
extern const struct quic_version *preferred_version;
|
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 */
|
/* unused: 0x01 */
|
||||||
/* Flag the packet number space as requiring an ACK frame to be sent. */
|
/* 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. */ \
|
/* Number of received bytes. */ \
|
||||||
uint64_t rx; \
|
uint64_t rx; \
|
||||||
} bytes; \
|
} bytes; \
|
||||||
|
size_t max_udp_payload; \
|
||||||
/* First DCID used by client on its Initial packet. */ \
|
/* First DCID used by client on its Initial packet. */ \
|
||||||
struct quic_cid odcid; \
|
struct quic_cid odcid; \
|
||||||
/* DCID of our endpoint - not updated when a new DCID is used */ \
|
/* DCID of our endpoint - not updated when a new DCID is used */ \
|
||||||
@ -315,7 +319,7 @@ struct qcc_app_ops;
|
|||||||
* with a connection \
|
* with a connection \
|
||||||
*/ \
|
*/ \
|
||||||
struct eb_root *cids; \
|
struct eb_root *cids; \
|
||||||
struct listener *li; /* only valid for frontend connections */ \
|
enum obj_type *target; \
|
||||||
/* Idle timer task */ \
|
/* Idle timer task */ \
|
||||||
struct task *idle_timer_task; \
|
struct task *idle_timer_task; \
|
||||||
unsigned int idle_expire; \
|
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_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_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_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 */
|
/* gap here */
|
||||||
#define QUIC_FL_CONN_TO_KILL (1U << 24) /* Unusable connection, to be killed */
|
#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) */
|
#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 quic_connection_id *conn_id,
|
||||||
struct sockaddr_storage *local_addr,
|
struct sockaddr_storage *local_addr,
|
||||||
struct sockaddr_storage *peer_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);
|
int quic_build_post_handshake_frames(struct quic_conn *qc);
|
||||||
const struct quic_version *qc_supported_version(uint32_t version);
|
const struct quic_version *qc_supported_version(uint32_t version);
|
||||||
int quic_peer_validated_addr(struct quic_conn *qc);
|
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;
|
*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 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_connection_close(struct quic_conn *qc, const struct quic_err err);
|
||||||
void quic_set_tls_alert(struct quic_conn *qc, int alert);
|
void quic_set_tls_alert(struct quic_conn *qc, int alert);
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
#include <haproxy/quic_rx-t.h>
|
#include <haproxy/quic_rx-t.h>
|
||||||
|
|
||||||
int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
|
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_treat_rx_pkts(struct quic_conn *qc);
|
||||||
int qc_parse_hd_form(struct quic_rx_packet *pkt,
|
int qc_parse_hd_form(struct quic_rx_packet *pkt,
|
||||||
unsigned char **pos, const unsigned char *end);
|
unsigned char **pos, const unsigned char *end);
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include <haproxy/connection-t.h>
|
#include <haproxy/connection-t.h>
|
||||||
#include <haproxy/fd-t.h>
|
#include <haproxy/fd-t.h>
|
||||||
#include <haproxy/listener-t.h>
|
#include <haproxy/listener-t.h>
|
||||||
|
#include <haproxy/obj_type.h>
|
||||||
#include <haproxy/quic_conn-t.h>
|
#include <haproxy/quic_conn-t.h>
|
||||||
#include <haproxy/quic_sock-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)
|
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
|
/* Try to increment <l> handshake current counter. If listener limit is
|
||||||
|
@ -34,7 +34,8 @@
|
|||||||
#include <haproxy/ssl_sock-t.h>
|
#include <haproxy/ssl_sock-t.h>
|
||||||
|
|
||||||
int ssl_quic_initial_ctx(struct bind_conf *bind_conf);
|
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 qc_ssl_provide_all_quic_data(struct quic_conn *qc, struct ssl_sock_ctx *ctx);
|
||||||
int quic_ssl_set_tls_cbs(SSL *ssl);
|
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 *dcid, size_t dcidlen,
|
||||||
const unsigned char *scid, size_t scidlen,
|
const unsigned char *scid, size_t scidlen,
|
||||||
const struct quic_cid *token_odcid);
|
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).
|
/* Dump <cid> transport parameter connection ID value if present (non null length).
|
||||||
* Used only for debugging purposes.
|
* Used only for debugging purposes.
|
||||||
|
@ -478,6 +478,9 @@ struct server {
|
|||||||
char *alpn_str; /* ALPN protocol string */
|
char *alpn_str; /* ALPN protocol string */
|
||||||
int alpn_len; /* ALPN protocol string length */
|
int alpn_len; /* ALPN protocol string length */
|
||||||
} ssl_ctx;
|
} 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 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 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 */
|
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 */
|
#endif /* _HAPROXY_SERVER_H */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -28,9 +28,11 @@
|
|||||||
#include <haproxy/api.h>
|
#include <haproxy/api.h>
|
||||||
#include <haproxy/connection-t.h>
|
#include <haproxy/connection-t.h>
|
||||||
#include <haproxy/listener-t.h>
|
#include <haproxy/listener-t.h>
|
||||||
|
#include <haproxy/protocol-t.h>
|
||||||
#include <haproxy/sock-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_enable(struct receiver *rx);
|
||||||
void sock_disable(struct receiver *rx);
|
void sock_disable(struct receiver *rx);
|
||||||
void sock_unbind(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,
|
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);
|
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,
|
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,
|
int ckch_inst_rebuild(struct ckch_store *ckch_store, struct ckch_inst *ckchi,
|
||||||
struct ckch_inst **new_inst, char **err);
|
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 */
|
/* set the correct protocol on the output stream connector */
|
||||||
|
|
||||||
if (srv) {
|
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);
|
conn_free(srv_conn);
|
||||||
return SF_ERR_INTERNAL;
|
return SF_ERR_INTERNAL;
|
||||||
}
|
}
|
||||||
|
@ -3767,6 +3767,8 @@ out_uri_auth_compat:
|
|||||||
((newsrv->flags & SRV_F_DEFSRV_USE_SSL) && newsrv->use_ssl != 1)) {
|
((newsrv->flags & SRV_F_DEFSRV_USE_SSL) && newsrv->use_ssl != 1)) {
|
||||||
if (xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->prepare_srv)
|
if (xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->prepare_srv)
|
||||||
cfgerr += xprt_get(XPRT_SSL)->prepare_srv(newsrv);
|
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) &&
|
if ((newsrv->flags & SRV_F_FASTOPEN) &&
|
||||||
|
@ -1423,7 +1423,8 @@ static int cli_io_handler_show_fd(struct appctx *appctx)
|
|||||||
#if defined(USE_QUIC)
|
#if defined(USE_QUIC)
|
||||||
else if (fdt.iocb == quic_conn_sock_fd_iocb) {
|
else if (fdt.iocb == quic_conn_sock_fd_iocb) {
|
||||||
qc = fdtab[fd].owner;
|
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;
|
xprt_ctx = qc ? qc->xprt_ctx : NULL;
|
||||||
conn = qc ? qc->conn : NULL;
|
conn = qc ? qc->conn : NULL;
|
||||||
xprt = conn ? conn->xprt : NULL; // in fact it's &ssl_quic
|
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/qmux_http.h>
|
||||||
#include <haproxy/qpack-dec.h>
|
#include <haproxy/qpack-dec.h>
|
||||||
#include <haproxy/qpack-enc.h>
|
#include <haproxy/qpack-enc.h>
|
||||||
|
#include <haproxy/quic_conn.h>
|
||||||
#include <haproxy/quic_enc.h>
|
#include <haproxy/quic_enc.h>
|
||||||
#include <haproxy/quic_fctl.h>
|
#include <haproxy/quic_fctl.h>
|
||||||
#include <haproxy/quic_frame.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)
|
static int h3_init(struct qcc *qcc)
|
||||||
{
|
{
|
||||||
struct h3c *h3c;
|
struct h3c *h3c;
|
||||||
const struct listener *li = __objt_listener(qcc->conn->target);
|
|
||||||
|
|
||||||
TRACE_ENTER(H3_EV_H3C_NEW, qcc->conn);
|
TRACE_ENTER(H3_EV_H3C_NEW, qcc->conn);
|
||||||
|
|
||||||
@ -2530,9 +2530,7 @@ static int h3_init(struct qcc *qcc)
|
|||||||
h3c->id_goaway = 0;
|
h3c->id_goaway = 0;
|
||||||
|
|
||||||
qcc->ctx = h3c;
|
qcc->ctx = h3c;
|
||||||
h3c->prx_counters =
|
h3c->prx_counters = qc_counters(qcc->conn->target, &h3_stats_module);
|
||||||
EXTRA_COUNTERS_GET(li->bind_conf->frontend->extra_counters_fe,
|
|
||||||
&h3_stats_module);
|
|
||||||
LIST_INIT(&h3c->buf_wait.list);
|
LIST_INIT(&h3c->buf_wait.list);
|
||||||
|
|
||||||
TRACE_LEAVE(H3_EV_H3C_NEW, qcc->conn);
|
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->stream = NULL;
|
||||||
qcs->qcc = qcc;
|
qcs->qcc = qcc;
|
||||||
|
qcs->sd = NULL;
|
||||||
|
qcs->sess = NULL;
|
||||||
qcs->flags = QC_SF_NONE;
|
qcs->flags = QC_SF_NONE;
|
||||||
qcs->st = QC_SS_IDLE;
|
qcs->st = QC_SS_IDLE;
|
||||||
qcs->ctx = NULL;
|
qcs->ctx = NULL;
|
||||||
@ -3406,6 +3408,7 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
|
|||||||
{
|
{
|
||||||
struct qcc *qcc;
|
struct qcc *qcc;
|
||||||
struct quic_transport_params *lparams, *rparams;
|
struct quic_transport_params *lparams, *rparams;
|
||||||
|
void *conn_ctx = conn->ctx;
|
||||||
|
|
||||||
TRACE_ENTER(QMUX_EV_QCC_NEW);
|
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. */
|
/* Register conn for idle front closing. This is done once everything is allocated. */
|
||||||
if (!conn_is_back(conn))
|
if (!conn_is_back(conn))
|
||||||
LIST_APPEND(&mux_stopping_data[tid].list, &conn->stopping_list);
|
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 */
|
/* init read cycle */
|
||||||
tasklet_wakeup(qcc->wait_event.tasklet);
|
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 =
|
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);
|
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 proxy *be;
|
||||||
struct conn_src *src;
|
struct conn_src *src;
|
||||||
struct sockaddr_storage *addr;
|
struct sockaddr_storage *addr;
|
||||||
|
struct quic_conn *qc = conn->handle.qc;
|
||||||
|
|
||||||
|
BUG_ON(qc->fd != -1);
|
||||||
BUG_ON(!conn->dst);
|
BUG_ON(!conn->dst);
|
||||||
|
|
||||||
conn->flags |= CO_FL_WAIT_L4_CONN; /* connection in progress */
|
|
||||||
|
|
||||||
switch (obj_type(conn->target)) {
|
switch (obj_type(conn->target)) {
|
||||||
case OBJ_TYPE_PROXY:
|
case OBJ_TYPE_PROXY:
|
||||||
be = __objt_proxy(conn->target);
|
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 */
|
/* 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)
|
if (fd == -1)
|
||||||
return stream_err;
|
return stream_err;
|
||||||
|
|
||||||
@ -420,64 +420,16 @@ int quic_connect_server(struct connection *conn, int flags)
|
|||||||
|
|
||||||
addr = (conn->flags & CO_FL_SOCKS4) ? &srv->socks4_addr : conn->dst;
|
addr = (conn->flags & CO_FL_SOCKS4) ? &srv->socks4_addr : conn->dst;
|
||||||
if (connect(fd, (const struct sockaddr *)addr, get_addr_len(addr)) == -1) {
|
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);
|
port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
|
||||||
fdinfo[fd].port_range = NULL;
|
fdinfo[fd].port_range = NULL;
|
||||||
close(fd);
|
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;
|
conn->flags |= CO_FL_ERROR;
|
||||||
return SF_ERR_SRVCL;
|
return SF_ERR_SRVCL;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* connect() == 0, this is great! */
|
|
||||||
conn->flags &= ~CO_FL_WAIT_L4_CONN;
|
|
||||||
}
|
|
||||||
|
|
||||||
conn_ctrl_init(conn); /* registers the FD */
|
qc->fd = fd;
|
||||||
HA_ATOMIC_OR(&fdtab[fd].state, FD_LINGER_RISK); /* close hard if needed */
|
fd_insert(fd, qc, quic_conn_sock_fd_iocb, tgid, ti->ltid_bit);
|
||||||
|
fd_want_recv(fd);
|
||||||
if (conn->flags & CO_FL_WAIT_L4_CONN) {
|
|
||||||
fd_want_send(fd);
|
|
||||||
fd_cant_send(fd);
|
|
||||||
fd_cant_recv(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SF_ERR_NONE; /* connection is OK */
|
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 */
|
/* 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)
|
if (fd == -1)
|
||||||
return stream_err;
|
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 */
|
/* 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)
|
if (fd == -1)
|
||||||
return stream_err;
|
return stream_err;
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include <haproxy/cli.h>
|
#include <haproxy/cli.h>
|
||||||
#include <haproxy/list.h>
|
#include <haproxy/list.h>
|
||||||
#include <haproxy/mux_quic.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_tp.h>
|
||||||
#include <haproxy/quic_utils.h>
|
#include <haproxy/quic_utils.h>
|
||||||
#include <haproxy/tools.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];
|
char bufaddr[INET6_ADDRSTRLEN], bufport[6];
|
||||||
int ret;
|
int ret;
|
||||||
unsigned char cid_len;
|
unsigned char cid_len;
|
||||||
|
struct listener *l = objt_listener(qc->target);
|
||||||
|
|
||||||
ret = chunk_appendf(&trash, "%p[%02u]/%-.12s ", qc, ctx->thr,
|
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 */
|
chunk_appendf(&trash, "%*s", 36 - ret, " "); /* align output */
|
||||||
|
|
||||||
/* State */
|
/* State */
|
||||||
|
163
src/quic_conn.c
163
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 */
|
/* 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;
|
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;
|
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->dcid = qc->dcid;
|
||||||
cc_qc->scid = qc->scid;
|
cc_qc->scid = qc->scid;
|
||||||
|
|
||||||
cc_qc->li = qc->li;
|
cc_qc->target = qc->target;
|
||||||
cc_qc->cids = qc->cids;
|
cc_qc->cids = qc->cids;
|
||||||
|
|
||||||
cc_qc->idle_timer_task = qc->idle_timer_task;
|
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 list send_list = LIST_HEAD_INIT(send_list);
|
||||||
struct quic_enc_level *qel;
|
struct quic_enc_level *qel;
|
||||||
int st;
|
int st;
|
||||||
|
int discard_hpktns = 0;
|
||||||
struct tasklet *tl = (struct tasklet *)t;
|
struct tasklet *tl = (struct tasklet *)t;
|
||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
|
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;
|
goto out;
|
||||||
|
|
||||||
st = qc->state;
|
st = qc->state;
|
||||||
if (st >= QUIC_HS_ST_COMPLETE) {
|
|
||||||
if (!(qc->flags & QUIC_FL_CONN_HPKTNS_DCD)) {
|
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. */
|
/* Discard the Handshake packet number space. */
|
||||||
TRACE_PROTO("discarding Handshake pktns", QUIC_EV_CONN_PHPKTS, qc);
|
TRACE_PROTO("discarding Handshake pktns", QUIC_EV_CONN_PHPKTS, qc);
|
||||||
quic_pktns_discard(qc->hel->pktns, qc);
|
quic_pktns_discard(qc->hel->pktns, qc);
|
||||||
qc_set_timer(qc);
|
qc_set_timer(qc);
|
||||||
qc_el_rx_pkts_del(qc->hel);
|
qc_el_rx_pkts_del(qc->hel);
|
||||||
qc_release_pktns_frms(qc, qc->hel->pktns);
|
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
|
/* Note: if no token for address validation was received
|
||||||
* for a 0RTT connection, some 0RTT packet could still be
|
* for a 0RTT connection, some 0RTT packet could still be
|
||||||
* waiting for HP removal AFTER the successful handshake completion.
|
* 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;
|
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:
|
out:
|
||||||
/* Release the Handshake encryption level and packet number space if
|
/* Release the Handshake encryption level and packet number space if
|
||||||
* the Handshake is confirmed and if there is no need to send
|
* 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 quic_connection_id *conn_id,
|
||||||
struct sockaddr_storage *local_addr,
|
struct sockaddr_storage *local_addr,
|
||||||
struct sockaddr_storage *peer_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 quic_conn *qc = NULL;
|
||||||
struct listener *l = server ? owner : NULL;
|
struct listener *l = objt_listener(target);
|
||||||
struct proxy *prx = l ? l->bind_conf->frontend : NULL;
|
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;
|
struct quic_cc_algo *cc_algo = NULL;
|
||||||
unsigned int next_actconn = 0, next_sslconn = 0, next_handshake = 0;
|
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;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server) {
|
if (l) {
|
||||||
next_handshake = quic_increment_curr_handshake(l);
|
next_handshake = quic_increment_curr_handshake(l);
|
||||||
if (!next_handshake) {
|
if (!next_handshake) {
|
||||||
TRACE_STATE("max handshake reached", QUIC_EV_CONN_INIT);
|
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;
|
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
|
/* Now that quic_conn instance is allocated, quic_conn_release() will
|
||||||
* ensure global accounting is decremented.
|
* 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;
|
qc->streams_by_id = EB_ROOT_UNIQUE;
|
||||||
|
|
||||||
/* Required to call free_quic_conn_cids() from quic_conn_release() */
|
/* Required to call free_quic_conn_cids() from quic_conn_release() */
|
||||||
qc->cids = NULL;
|
|
||||||
qc->tx.cc_buf_area = NULL;
|
qc->tx.cc_buf_area = NULL;
|
||||||
qc_init_fd(qc);
|
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->idle_timer_task = NULL;
|
||||||
|
|
||||||
qc->xprt_ctx = NULL;
|
qc->xprt_ctx = NULL;
|
||||||
qc->conn = NULL;
|
qc->conn = conn;
|
||||||
qc->qcc = NULL;
|
qc->qcc = NULL;
|
||||||
qc->app_ops = NULL;
|
qc->app_ops = NULL;
|
||||||
qc->path = NULL;
|
qc->path = NULL;
|
||||||
@ -1117,16 +1165,12 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
/* Packet number spaces */
|
/* Packet number spaces */
|
||||||
qc->ipktns = qc->hpktns = qc->apktns = NULL;
|
qc->ipktns = qc->hpktns = qc->apktns = NULL;
|
||||||
LIST_INIT(&qc->pktns_list);
|
LIST_INIT(&qc->pktns_list);
|
||||||
|
qc->prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module);
|
||||||
/* Required to safely call quic_conn_prx_cntrs_update() from quic_conn_release(). */
|
|
||||||
qc->prx_counters = NULL;
|
|
||||||
|
|
||||||
/* QUIC Server (or listener). */
|
/* QUIC Server (or listener). */
|
||||||
if (server) {
|
if (l) {
|
||||||
cc_algo = l->bind_conf->quic_cc_algo;
|
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;
|
qc->flags = QUIC_FL_CONN_LISTENER;
|
||||||
/* Mark this connection as having not received any token when 0-RTT is enabled. */
|
/* Mark this connection as having not received any token when 0-RTT is enabled. */
|
||||||
if (l->bind_conf->ssl_conf.early_data && !token)
|
if (l->bind_conf->ssl_conf.early_data && !token)
|
||||||
@ -1137,23 +1181,45 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
/* Copy the packet SCID to reuse it as DCID for sending */
|
/* Copy the packet SCID to reuse it as DCID for sending */
|
||||||
qc->dcid = *scid;
|
qc->dcid = *scid;
|
||||||
qc->tx.buf = BUF_NULL;
|
qc->tx.buf = BUF_NULL;
|
||||||
qc->li = l;
|
conn_id->qc = qc;
|
||||||
}
|
}
|
||||||
/* QUIC Client (outgoing connection to servers) */
|
/* QUIC Client (outgoing connection to servers) */
|
||||||
else {
|
else {
|
||||||
|
struct quic_connection_id *conn_cid = NULL;
|
||||||
|
|
||||||
|
qc->flags = QUIC_FL_CONN_PEER_VALIDATED_ADDR;
|
||||||
qc->state = QUIC_HS_ST_CLIENT_INITIAL;
|
qc->state = QUIC_HS_ST_CLIENT_INITIAL;
|
||||||
if (dcid->len)
|
|
||||||
memcpy(qc->dcid.data, dcid->data, dcid->len);
|
memset(&qc->odcid, 0, sizeof qc->odcid);
|
||||||
qc->dcid.len = dcid->len;
|
qc->odcid.len = 0;
|
||||||
qc->li = NULL;
|
/* 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->mux_state = QC_MUX_NULL;
|
||||||
qc->err = quic_err_transport(QC_ERR_NO_ERROR);
|
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.
|
* already checked token, consider the peer address as validated.
|
||||||
*/
|
*/
|
||||||
if (token) {
|
if (l) {
|
||||||
|
if (token_odcid->len) {
|
||||||
TRACE_STATE("validate peer address due to initial token",
|
TRACE_STATE("validate peer address due to initial token",
|
||||||
QUIC_EV_CONN_INIT, qc);
|
QUIC_EV_CONN_INIT, qc);
|
||||||
qc->flags |= QUIC_FL_CONN_PEER_VALIDATED_ADDR;
|
qc->flags |= QUIC_FL_CONN_PEER_VALIDATED_ADDR;
|
||||||
@ -1161,6 +1227,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
else {
|
else {
|
||||||
HA_ATOMIC_INC(&qc->prx_counters->half_open_conn);
|
HA_ATOMIC_INC(&qc->prx_counters->half_open_conn);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Now proceeds to allocation of qc members. */
|
/* Now proceeds to allocation of qc members. */
|
||||||
qc->rx.buf.area = pool_alloc(pool_head_quic_conn_rxbuf);
|
qc->rx.buf.area = pool_alloc(pool_head_quic_conn_rxbuf);
|
||||||
@ -1169,16 +1236,8 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
qc->cids = pool_alloc(pool_head_quic_cids);
|
/* Listener only */
|
||||||
if (!qc->cids) {
|
if (l && HA_ATOMIC_LOAD(&l->rx.quic_mode) == QUIC_SOCK_MODE_CONN &&
|
||||||
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 &&
|
|
||||||
(quic_tune.options & QUIC_TUNE_SOCK_PER_CONN) &&
|
(quic_tune.options & QUIC_TUNE_SOCK_PER_CONN) &&
|
||||||
is_addr(local_addr)) {
|
is_addr(local_addr)) {
|
||||||
TRACE_USER("Allocate a socket for QUIC connection", QUIC_EV_CONN_INIT, qc);
|
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.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->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_for_cc = 1;
|
||||||
qc->nb_pkt_since_cc = 0;
|
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;
|
qc->max_ack_delay = 0;
|
||||||
/* Only one path at this time (multipath not supported) */
|
/* Only one path at this time (multipath not supported) */
|
||||||
qc->path = &qc->paths[0];
|
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);
|
cc_algo ? cc_algo : default_quic_cc_algo, qc);
|
||||||
|
|
||||||
|
if (local_addr)
|
||||||
memcpy(&qc->local_addr, local_addr, sizeof(qc->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);
|
memcpy(&qc->peer_addr, peer_addr, sizeof qc->peer_addr);
|
||||||
|
|
||||||
if (server && !qc_lstnr_params_init(qc, &l->bind_conf->quic_params,
|
if (l) {
|
||||||
|
qc_lstnr_params_init(qc, &l->bind_conf->quic_params,
|
||||||
conn_id->stateless_reset_token,
|
conn_id->stateless_reset_token,
|
||||||
dcid->data, dcid->len,
|
qc->dcid.data, qc->dcid.len,
|
||||||
qc->scid.data, qc->scid.len, token_odcid))
|
qc->scid.data, qc->scid.len, token_odcid);
|
||||||
goto err;
|
}
|
||||||
|
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"
|
/* Initialize the idle timeout of the connection at the "max_idle_timeout"
|
||||||
* value from local transport parameters.
|
* 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->wait_event.events = 0;
|
||||||
qc->subs = NULL;
|
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_timer(qc) ||
|
||||||
!quic_conn_init_idle_timer_task(qc, prx))
|
!quic_conn_init_idle_timer_task(qc, prx))
|
||||||
goto err;
|
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;
|
goto err;
|
||||||
|
|
||||||
/* Counters initialization */
|
/* Counters initialization */
|
||||||
@ -1278,6 +1348,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
return qc;
|
return qc;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
pool_free(pool_head_quic_connection_id, conn_id);
|
||||||
quic_conn_release(qc);
|
quic_conn_release(qc);
|
||||||
|
|
||||||
/* Decrement global counters. Done only for errors happening before or
|
/* 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
|
* used during the handshake, unless the endpoint has acted on a
|
||||||
* preferred_address transport parameter from the peer.
|
* 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);
|
TRACE_ERROR("Active migration was disabled, datagram dropped", QUIC_EV_CONN_LPKT, qc);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
@ -1444,8 +1515,8 @@ int quic_conn_release(struct quic_conn *qc)
|
|||||||
*/
|
*/
|
||||||
if (MT_LIST_INLIST(&qc->accept_list)) {
|
if (MT_LIST_INLIST(&qc->accept_list)) {
|
||||||
MT_LIST_DELETE(&qc->accept_list);
|
MT_LIST_DELETE(&qc->accept_list);
|
||||||
BUG_ON(qc->li->rx.quic_curr_accept == 0);
|
BUG_ON(__objt_listener(qc->target)->rx.quic_curr_accept == 0);
|
||||||
HA_ATOMIC_DEC(&qc->li->rx.quic_curr_accept);
|
HA_ATOMIC_DEC(&__objt_listener(qc->target)->rx.quic_curr_accept);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Substract last congestion window from global memory counter. */
|
/* 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. */
|
/* Connection released before handshake completion. */
|
||||||
if (unlikely(qc->state < QUIC_HS_ST_COMPLETE)) {
|
if (unlikely(qc->state < QUIC_HS_ST_COMPLETE)) {
|
||||||
if (qc_is_listener(qc)) {
|
if (qc_is_listener(qc)) {
|
||||||
BUG_ON(qc->li->rx.quic_curr_handshake == 0);
|
BUG_ON(__objt_listener(qc->target)->rx.quic_curr_handshake == 0);
|
||||||
HA_ATOMIC_DEC(&qc->li->rx.quic_curr_handshake);
|
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
|
/* At this point no connection was accounted for yet on this
|
||||||
* listener so it's OK to just swap the pointer.
|
* listener so it's OK to just swap the pointer.
|
||||||
*/
|
*/
|
||||||
if (new_li && new_li != qc->li)
|
if (new_li && new_li != __objt_listener(qc->target))
|
||||||
qc->li = new_li;
|
qc->target = &new_li->obj_type;
|
||||||
|
|
||||||
/* Rebind the connection FD. */
|
/* Rebind the connection FD. */
|
||||||
if (qc_test_fd(qc)) {
|
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)
|
int quic_tls_compat_init(struct bind_conf *bind_conf, SSL_CTX *ctx)
|
||||||
{
|
{
|
||||||
/* Ignore non-QUIC connections */
|
/* Ignore non-QUIC connections */
|
||||||
if (bind_conf->xprt != xprt_get(XPRT_QUIC))
|
if (bind_conf && bind_conf->xprt != xprt_get(XPRT_QUIC))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* This callback is already registered if the TLS keylog is activated for
|
/* 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 &&
|
if (sizeof(QUIC_OPENSSL_COMPAT_CLIENT_HANDSHAKE) - 1 == n &&
|
||||||
!strncmp(start, QUIC_OPENSSL_COMPAT_CLIENT_HANDSHAKE, n)) {
|
!strncmp(start, QUIC_OPENSSL_COMPAT_CLIENT_HANDSHAKE, n)) {
|
||||||
level = ssl_encryption_handshake;
|
level = ssl_encryption_handshake;
|
||||||
write = 0;
|
write = qc_is_listener(qc) ? 0 : 1;
|
||||||
}
|
}
|
||||||
else if (sizeof(QUIC_OPENSSL_COMPAT_SERVER_HANDSHAKE) - 1 == n &&
|
else if (sizeof(QUIC_OPENSSL_COMPAT_SERVER_HANDSHAKE) - 1 == n &&
|
||||||
!strncmp(start, QUIC_OPENSSL_COMPAT_SERVER_HANDSHAKE, n)) {
|
!strncmp(start, QUIC_OPENSSL_COMPAT_SERVER_HANDSHAKE, n)) {
|
||||||
level = ssl_encryption_handshake;
|
level = ssl_encryption_handshake;
|
||||||
write = 1;
|
write = qc_is_listener(qc) ? 1 : 0;
|
||||||
}
|
}
|
||||||
else if (sizeof(QUIC_OPENSSL_COMPAT_CLIENT_APPLICATION) - 1 == n &&
|
else if (sizeof(QUIC_OPENSSL_COMPAT_CLIENT_APPLICATION) - 1 == n &&
|
||||||
!strncmp(start, QUIC_OPENSSL_COMPAT_CLIENT_APPLICATION, n)) {
|
!strncmp(start, QUIC_OPENSSL_COMPAT_CLIENT_APPLICATION, n)) {
|
||||||
level = ssl_encryption_application;
|
level = ssl_encryption_application;
|
||||||
write = 0;
|
write = qc_is_listener(qc) ? 0 : 1;
|
||||||
}
|
}
|
||||||
else if (sizeof(QUIC_OPENSSL_COMPAT_SERVER_APPLICATION) - 1 == n &&
|
else if (sizeof(QUIC_OPENSSL_COMPAT_SERVER_APPLICATION) - 1 == n &&
|
||||||
!strncmp(start, QUIC_OPENSSL_COMPAT_SERVER_APPLICATION, n)) {
|
!strncmp(start, QUIC_OPENSSL_COMPAT_SERVER_APPLICATION, n)) {
|
||||||
level = ssl_encryption_application;
|
level = ssl_encryption_application;
|
||||||
write = 1;
|
write = qc_is_listener(qc) ? 1 : 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
goto leave;
|
goto leave;
|
||||||
|
@ -1819,8 +1819,8 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt,
|
|||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
qc = qc_new_conn(pkt->version, ipv4, &pkt->dcid, &pkt->scid, &token_odcid,
|
qc = qc_new_conn(pkt->version, ipv4, &pkt->dcid, &pkt->scid, &token_odcid,
|
||||||
conn_id, &dgram->daddr, &pkt->saddr, 1,
|
conn_id, &dgram->daddr, &pkt->saddr,
|
||||||
!!pkt->token_len, l);
|
!!pkt->token_len, l, NULL);
|
||||||
if (qc == NULL) {
|
if (qc == NULL) {
|
||||||
pool_free(pool_head_quic_connection_id, conn_id);
|
pool_free(pool_head_quic_connection_id, conn_id);
|
||||||
goto err;
|
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
|
/* 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>
|
* if the packet is incomplete. This function will populate fields of <pkt>
|
||||||
* instance, most notably its length. <dgram> is the UDP datagram which
|
* instance, most notably its length. <dgram> is the UDP datagram which
|
||||||
* contains the parsed packet. <l> is the listener instance on which it was
|
* contains the parsed packet. <o> is the address object type address of the
|
||||||
* received.
|
* 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
|
* 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
|
* the real packet value or to cover all data between <pos> and <end> : this is
|
||||||
* useful to reject a whole datagram.
|
* 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,
|
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;
|
const unsigned char *beg = pos;
|
||||||
struct proxy *prx;
|
|
||||||
struct quic_counters *prx_counters;
|
struct quic_counters *prx_counters;
|
||||||
|
struct listener *l = objt_listener(o);
|
||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_LPKT);
|
TRACE_ENTER(QUIC_EV_CONN_LPKT);
|
||||||
|
|
||||||
prx = l->bind_conf->frontend;
|
prx_counters = qc_counters(o, &quic_stats_module);
|
||||||
prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module);
|
|
||||||
|
|
||||||
if (end <= pos) {
|
if (end <= pos) {
|
||||||
TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT);
|
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;
|
goto drop;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RFC9000 6. Version Negotiation */
|
/* RFC9000 6. Version Negotiation. A Version Negotiation packet is
|
||||||
if (!pkt->version) {
|
* sent only by servers.
|
||||||
|
*/
|
||||||
|
if (l && !pkt->version) {
|
||||||
/* unsupported version, send Negotiation packet */
|
/* unsupported version, send Negotiation packet */
|
||||||
if (send_version_negotiation(l->rx.fd, &dgram->saddr, pkt)) {
|
if (send_version_negotiation(l->rx.fd, &dgram->saddr, pkt)) {
|
||||||
TRACE_ERROR("VN packet not sent", QUIC_EV_CONN_LPKT);
|
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;
|
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 = pos;
|
||||||
pkt->token_len = token_len;
|
pkt->token_len = token_len;
|
||||||
pos += pkt->token_len;
|
pos += pkt->token_len;
|
||||||
@ -2001,28 +2010,51 @@ static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
|
|||||||
pkt->pn_offset = pos - beg;
|
pkt->pn_offset = pos - beg;
|
||||||
pkt->len = pkt->pn_offset + len;
|
pkt->len = pkt->pn_offset + len;
|
||||||
|
|
||||||
|
/* 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_INITIAL) {
|
||||||
|
if (l) {
|
||||||
/* RFC 9000. Initial Datagram Size
|
/* RFC 9000. Initial Datagram Size
|
||||||
*
|
*
|
||||||
* A server MUST discard an Initial packet that is carried in a UDP datagram
|
* 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
|
* with a payload that is smaller than the smallest allowed maximum datagram
|
||||||
* size of 1200 bytes.
|
* size of 1200 bytes.
|
||||||
*/
|
*/
|
||||||
if (pkt->type == QUIC_PACKET_TYPE_INITIAL &&
|
if (dgram->len < QUIC_INITIAL_PACKET_MINLEN) {
|
||||||
dgram->len < QUIC_INITIAL_PACKET_MINLEN) {
|
|
||||||
TRACE_PROTO("RX too short datagram with an Initial packet", QUIC_EV_CONN_LPKT);
|
TRACE_PROTO("RX too short datagram with an Initial packet", QUIC_EV_CONN_LPKT);
|
||||||
HA_ATOMIC_INC(&prx_counters->too_short_initial_dgram);
|
HA_ATOMIC_INC(&prx_counters->too_short_initial_dgram);
|
||||||
goto drop;
|
goto drop;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/* Interrupt parsing after packet length retrieval : this
|
else {
|
||||||
* ensures that only the packet is dropped but not the whole
|
/* TODO: This is not clear if a too short RX datagram which carries ack-eliciting
|
||||||
* datagram.
|
* 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 (pkt->type == QUIC_PACKET_TYPE_0RTT && !l->bind_conf->ssl_conf.early_data) {
|
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);
|
TRACE_PROTO("RX 0-RTT packet not supported", QUIC_EV_CONN_LPKT);
|
||||||
goto drop;
|
goto drop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
TRACE_PROTO("RX short header packet", QUIC_EV_CONN_LPKT);
|
TRACE_PROTO("RX short header packet", QUIC_EV_CONN_LPKT);
|
||||||
if (end - pos < QUIC_HAP_CID_LEN) {
|
if (end - pos < QUIC_HAP_CID_LEN) {
|
||||||
@ -2242,7 +2274,8 @@ static void qc_rx_pkt_handle(struct quic_conn *qc, struct quic_rx_packet *pkt,
|
|||||||
* this function.
|
* this function.
|
||||||
*
|
*
|
||||||
* If datagram has been received on a quic-conn owned FD, <from_qc> must be set
|
* 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
|
* responsible to ensure that the first packet is destined to this connection
|
||||||
* by comparing CIDs.
|
* 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.
|
* the datagram may not have been parsed.
|
||||||
*/
|
*/
|
||||||
int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
|
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_rx_packet *pkt;
|
||||||
struct quic_conn *qc = NULL;
|
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;
|
pkt->flags |= QUIC_FL_RX_PACKET_DGRAM_FIRST;
|
||||||
|
|
||||||
quic_rx_packet_refinc(pkt);
|
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;
|
goto next;
|
||||||
|
|
||||||
/* Search quic-conn instance for first packet of the datagram.
|
/* 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) {
|
if (!qc) {
|
||||||
int new_tid = -1;
|
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 = 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
|
/* 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);
|
memcpy(addr, &qc->peer_addr, len);
|
||||||
} else {
|
} else {
|
||||||
struct sockaddr_storage *from;
|
struct sockaddr_storage *from;
|
||||||
|
struct listener *l = objt_listener(qc->target);
|
||||||
|
|
||||||
/* Return listener address if IP_PKTINFO or friends are not
|
/* Return listener address if IP_PKTINFO or friends are not
|
||||||
* supported by the socket.
|
* supported by the socket.
|
||||||
*/
|
*/
|
||||||
BUG_ON(!qc->li);
|
BUG_ON(!l);
|
||||||
from = is_addr(&qc->local_addr) ? &qc->local_addr :
|
from = is_addr(&qc->local_addr) ? &qc->local_addr : &l->rx.addr;
|
||||||
&qc->li->rx.addr;
|
|
||||||
if (len > sizeof(*from))
|
if (len > sizeof(*from))
|
||||||
len = sizeof(*from);
|
len = sizeof(*from);
|
||||||
memcpy(addr, from, len);
|
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)
|
int qc_rcv_buf(struct quic_conn *qc)
|
||||||
{
|
{
|
||||||
struct sockaddr_storage saddr = {0}, daddr = {0};
|
struct sockaddr_storage saddr = {0}, daddr = {0};
|
||||||
struct quic_transport_params *params;
|
|
||||||
struct quic_dgram *new_dgram = NULL;
|
struct quic_dgram *new_dgram = NULL;
|
||||||
struct buffer buf = BUF_NULL;
|
struct buffer buf = BUF_NULL;
|
||||||
size_t max_sz;
|
|
||||||
unsigned char *dgram_buf;
|
unsigned char *dgram_buf;
|
||||||
struct listener *l;
|
|
||||||
ssize_t ret = 0;
|
ssize_t ret = 0;
|
||||||
|
struct listener *l = objt_listener(qc->target);
|
||||||
|
|
||||||
/* Do not call this if quic-conn FD is uninitialized. */
|
/* Do not call this if quic-conn FD is uninitialized. */
|
||||||
BUG_ON(qc->fd < 0);
|
BUG_ON(qc->fd < 0);
|
||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_RCV, qc);
|
TRACE_ENTER(QUIC_EV_CONN_RCV, qc);
|
||||||
l = qc->li;
|
|
||||||
|
|
||||||
params = &l->bind_conf->quic_params;
|
|
||||||
max_sz = params->max_udp_payload_size;
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (!b_alloc(&buf, DB_MUX_RX))
|
if (!b_alloc(&buf, DB_MUX_RX))
|
||||||
break; /* TODO subscribe for memory again available. */
|
break; /* TODO subscribe for memory again available. */
|
||||||
|
|
||||||
b_reset(&buf);
|
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. */
|
/* Allocate datagram on first loop or after requeuing. */
|
||||||
if (!new_dgram && !(new_dgram = pool_alloc(pool_head_quic_dgram)))
|
if (!new_dgram && !(new_dgram = pool_alloc(pool_head_quic_dgram)))
|
||||||
break; /* TODO subscribe for memory again available. */
|
break; /* TODO subscribe for memory again available. */
|
||||||
|
|
||||||
dgram_buf = (unsigned char *)b_tail(&buf);
|
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 *)&saddr, sizeof(saddr),
|
||||||
(struct sockaddr *)&daddr, sizeof(daddr),
|
(struct sockaddr *)&daddr, sizeof(daddr),
|
||||||
get_net_port(&qc->local_addr));
|
get_net_port(&qc->local_addr));
|
||||||
@ -876,7 +870,7 @@ int qc_rcv_buf(struct quic_conn *qc)
|
|||||||
continue;
|
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
|
/* Datagram received by error on the connection FD, dispatch it
|
||||||
* to its associated quic-conn.
|
* to its associated quic-conn.
|
||||||
*
|
*
|
||||||
@ -934,7 +928,7 @@ int qc_rcv_buf(struct quic_conn *qc)
|
|||||||
continue;
|
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(). */
|
/* A datagram must always be consumed after quic_parse_dgram(). */
|
||||||
BUG_ON(new_dgram->buf);
|
BUG_ON(new_dgram->buf);
|
||||||
} while (ret > 0);
|
} 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
|
* Return the socket FD or a negative error code. On error, socket is marked as
|
||||||
* uninitialized.
|
* uninitialized.
|
||||||
|
* Note: This function must not be run for backends connection.
|
||||||
*/
|
*/
|
||||||
void qc_alloc_fd(struct quic_conn *qc, const struct sockaddr_storage *src,
|
void qc_alloc_fd(struct quic_conn *qc, const struct sockaddr_storage *src,
|
||||||
const struct sockaddr_storage *dst)
|
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;
|
struct proxy *p = bc->frontend;
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
int ret;
|
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. */
|
/* 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;
|
goto err;
|
||||||
}
|
}
|
||||||
@ -1067,13 +1063,14 @@ struct quic_accept_queue *quic_accept_queues;
|
|||||||
void quic_accept_push_qc(struct quic_conn *qc)
|
void quic_accept_push_qc(struct quic_conn *qc)
|
||||||
{
|
{
|
||||||
struct quic_accept_queue *queue = &quic_accept_queues[tid];
|
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. */
|
/* A connection must only be accepted once per instance. */
|
||||||
BUG_ON(qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED);
|
BUG_ON(qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED);
|
||||||
|
|
||||||
BUG_ON(MT_LIST_INLIST(&qc->accept_list));
|
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;
|
qc->flags |= QUIC_FL_CONN_ACCEPT_REGISTERED;
|
||||||
/* 1. insert the listener in the accept queue
|
/* 1. insert the listener in the accept queue
|
||||||
|
141
src/quic_ssl.c
141
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.
|
* be set to 1 for a QUIC server, 0 for a client.
|
||||||
* Return 1 if succeeded, 0 if not.
|
* 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)
|
const struct quic_version *ver, int server)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@ -40,7 +40,7 @@ static int qc_ssl_set_quic_transport_params(struct quic_conn *qc,
|
|||||||
goto leave;
|
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);
|
TRACE_ERROR("SSL_set_quic_transport_params() failed", QUIC_EV_CONN_RWSEC);
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
@ -274,16 +274,30 @@ write:
|
|||||||
|
|
||||||
/* Set the transport parameters in the TLS stack. */
|
/* Set the transport parameters in the TLS stack. */
|
||||||
if (level == ssl_encryption_handshake && qc_is_listener(qc) &&
|
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;
|
goto leave;
|
||||||
|
|
||||||
keyupdate_init:
|
keyupdate_init:
|
||||||
/* Store the secret provided by the TLS stack, required for keyupdate. */
|
|
||||||
if (level == ssl_encryption_application) {
|
if (level == ssl_encryption_application) {
|
||||||
struct quic_tls_kp *prv_rx = &qc->ku.prv_rx;
|
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_rx = &qc->ku.nxt_rx;
|
||||||
struct quic_tls_kp *nxt_tx = &qc->ku.nxt_tx;
|
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) {
|
||||||
if (!(rx->secret = pool_alloc(pool_head_quic_tls_secret))) {
|
if (!(rx->secret = pool_alloc(pool_head_quic_tls_secret))) {
|
||||||
TRACE_ERROR("Could not allocate RX Application secrete keys", QUIC_EV_CONN_RWSEC, qc);
|
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 {
|
else {
|
||||||
if (!quic_transport_params_store(qc, 0, params, params + params_len) ||
|
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;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -754,6 +768,44 @@ int ssl_quic_initial_ctx(struct bind_conf *bind_conf)
|
|||||||
return cfgerr;
|
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
|
/* 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
|
* if the debug mode and the verbose mode are activated. It dump all
|
||||||
* the SSL error until the stack was empty.
|
* the SSL error until the stack was empty.
|
||||||
@ -885,7 +937,8 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
|
|||||||
* provided by the stack. This happens after having received the peer
|
* provided by the stack. This happens after having received the peer
|
||||||
* handshake level CRYPTO data which are validated by the TLS stack.
|
* handshake level CRYPTO data which are validated by the TLS stack.
|
||||||
*/
|
*/
|
||||||
if (qc->li->bind_conf->ssl_conf.early_data &&
|
if (qc_is_listener(qc)) {
|
||||||
|
if (__objt_listener(qc->target)->bind_conf->ssl_conf.early_data &&
|
||||||
(!qc->ael || !qc->ael->tls_ctx.rx.secret)) {
|
(!qc->ael || !qc->ael->tls_ctx.rx.secret)) {
|
||||||
TRACE_PROTO("SSL handshake in progress",
|
TRACE_PROTO("SSL handshake in progress",
|
||||||
QUIC_EV_CONN_IO_CB, qc, &state, &ssl_err);
|
QUIC_EV_CONN_IO_CB, qc, &state, &ssl_err);
|
||||||
@ -894,19 +947,44 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
|
|||||||
else {
|
else {
|
||||||
TRACE_PROTO("SSL handshake OK", QUIC_EV_CONN_IO_CB, qc, &state);
|
TRACE_PROTO("SSL handshake OK", QUIC_EV_CONN_IO_CB, qc, &state);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Check the alpn could be negotiated */
|
/* Check the alpn could be negotiated */
|
||||||
|
if (qc_is_listener(qc)) {
|
||||||
if (!qc->app_ops) {
|
if (!qc->app_ops) {
|
||||||
TRACE_ERROR("No negotiated ALPN", QUIC_EV_CONN_IO_CB, qc, &state);
|
TRACE_ERROR("No negotiated ALPN", QUIC_EV_CONN_IO_CB, qc, &state);
|
||||||
quic_set_tls_alert(qc, SSL_AD_NO_APPLICATION_PROTOCOL);
|
quic_set_tls_alert(qc, SSL_AD_NO_APPLICATION_PROTOCOL);
|
||||||
goto leave;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
qc->flags |= QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS;
|
||||||
|
if (qc_is_listener(ctx->qc)) {
|
||||||
|
struct listener *l = __objt_listener(qc->target);
|
||||||
/* I/O callback switch */
|
/* I/O callback switch */
|
||||||
qc->wait_event.tasklet->process = quic_conn_app_io_cb;
|
qc->wait_event.tasklet->process = quic_conn_app_io_cb;
|
||||||
if (qc_is_listener(ctx->qc)) {
|
|
||||||
qc->flags |= QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS;
|
|
||||||
qc->state = QUIC_HS_ST_CONFIRMED;
|
qc->state = QUIC_HS_ST_CONFIRMED;
|
||||||
|
|
||||||
if (!(qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED)) {
|
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);
|
tasklet_wakeup(qc->wait_event.tasklet);
|
||||||
}
|
}
|
||||||
|
|
||||||
BUG_ON(qc->li->rx.quic_curr_handshake == 0);
|
BUG_ON(l->rx.quic_curr_handshake == 0);
|
||||||
HA_ATOMIC_DEC(&qc->li->rx.quic_curr_handshake);
|
HA_ATOMIC_DEC(&l->rx.quic_curr_handshake);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
qc->state = QUIC_HS_ST_COMPLETE;
|
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
|
* Return 0 if succeeded, -1 if not. If failed, sets the ->err_code member of <qc->conn> to
|
||||||
* CO_ER_SSL_NO_MEM.
|
* 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;
|
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) ||
|
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)) {
|
!quic_ssl_set_tls_cbs(*ssl)) {
|
||||||
SSL_free(*ssl);
|
SSL_free(*ssl);
|
||||||
*ssl = NULL;
|
*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.
|
* 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;
|
int ret = 0;
|
||||||
struct bind_conf *bc = qc->li->bind_conf;
|
|
||||||
struct ssl_sock_ctx *ctx = NULL;
|
struct ssl_sock_ctx *ctx = NULL;
|
||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
|
TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
|
||||||
@ -1135,7 +1214,7 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->conn = NULL;
|
ctx->conn = conn;
|
||||||
ctx->bio = NULL;
|
ctx->bio = NULL;
|
||||||
ctx->xprt = NULL;
|
ctx->xprt = NULL;
|
||||||
ctx->xprt_ctx = NULL;
|
ctx->xprt_ctx = NULL;
|
||||||
@ -1148,7 +1227,9 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc)
|
|||||||
ctx->qc = qc;
|
ctx->qc = qc;
|
||||||
|
|
||||||
if (qc_is_listener(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;
|
goto err;
|
||||||
#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L) && defined(HAVE_SSL_0RTT_QUIC)
|
#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L) && defined(HAVE_SSL_0RTT_QUIC)
|
||||||
/* Enabling 0-RTT */
|
/* Enabling 0-RTT */
|
||||||
@ -1158,6 +1239,36 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc)
|
|||||||
|
|
||||||
SSL_set_accept_state(ctx->ssl);
|
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);
|
ctx->xprt = xprt_get(XPRT_QUIC);
|
||||||
|
|
||||||
|
@ -213,9 +213,18 @@ static int quic_transport_param_dec_version_info(struct tp_version_information *
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server)
|
if (server) {
|
||||||
/* TODO: not supported */
|
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;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
for (ver = others; ver < end; ver += 4) {
|
for (ver = others; ver < end; ver += 4) {
|
||||||
if (!tp->negotiated_version) {
|
if (!tp->negotiated_version) {
|
||||||
@ -513,11 +522,24 @@ static int quic_transport_param_enc_version_info(unsigned char **buf,
|
|||||||
memcpy(*buf, &ver, sizeof ver);
|
memcpy(*buf, &ver, sizeof ver);
|
||||||
*buf += sizeof ver;
|
*buf += sizeof ver;
|
||||||
/* For servers: all supported version, chosen included */
|
/* For servers: all supported version, chosen included */
|
||||||
|
if (server) {
|
||||||
for (i = 0; i < quic_versions_nb; i++) {
|
for (i = 0; i < quic_versions_nb; i++) {
|
||||||
ver = htonl(quic_versions[i].num);
|
ver = htonl(quic_versions[i].num);
|
||||||
memcpy(*buf, &ver, sizeof ver);
|
memcpy(*buf, &ver, sizeof ver);
|
||||||
*buf += 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;
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -806,3 +828,21 @@ int qc_lstnr_params_init(struct quic_conn *qc,
|
|||||||
return 1;
|
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",
|
chunk_appendf(&trace_buf, " : qc@%p idle_timer_task@%p flags=0x%x",
|
||||||
qc, qc->idle_timer_task, qc->flags);
|
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) {
|
if (mask & QUIC_EV_CONN_INIT) {
|
||||||
chunk_appendf(&trace_buf, "\n odcid");
|
chunk_appendf(&trace_buf, "\n odcid");
|
||||||
quic_cid_dump(&trace_buf, &qc->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;
|
int ret = 0;
|
||||||
struct quic_conn *qc;
|
struct quic_conn *qc;
|
||||||
char skip_sendto = 0;
|
char skip_sendto = 0;
|
||||||
|
struct listener *l;
|
||||||
|
|
||||||
qc = ctx->qc;
|
qc = ctx->qc;
|
||||||
|
l = objt_listener(qc->target);
|
||||||
TRACE_ENTER(QUIC_EV_CONN_SPPKTS, qc);
|
TRACE_ENTER(QUIC_EV_CONN_SPPKTS, qc);
|
||||||
while (b_contig_data(buf, 0)) {
|
while (b_contig_data(buf, 0)) {
|
||||||
unsigned char *pos;
|
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 datagram bigger than MTU, several ones were encoded for GSO usage. */
|
||||||
if (dglen > qc->path->mtu) {
|
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);
|
TRACE_PROTO("send multiple datagrams with GSO", QUIC_EV_CONN_SPPKTS, qc);
|
||||||
gso = qc->path->mtu;
|
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);
|
int ret = qc_snd_buf(qc, &tmpbuf, tmpbuf.data, 0, gso);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (gso && ret == -EIO) {
|
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.
|
/* Disable permanently UDP GSO for this listener.
|
||||||
* Retry standard emission.
|
* Retry standard emission.
|
||||||
*/
|
*/
|
||||||
TRACE_ERROR("mark listener UDP GSO as unsupported", QUIC_EV_CONN_SPPKTS, qc, first_pkt);
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -576,6 +586,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
|
|||||||
int dgram_cnt = 0;
|
int dgram_cnt = 0;
|
||||||
/* Restrict GSO emission to comply with sendmsg limitation. See QUIC_MAX_GSO_DGRAMS for more details. */
|
/* Restrict GSO emission to comply with sendmsg limitation. See QUIC_MAX_GSO_DGRAMS for more details. */
|
||||||
uchar gso_dgram_cnt = 0;
|
uchar gso_dgram_cnt = 0;
|
||||||
|
struct listener *l = objt_listener(qc->target);
|
||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
|
TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
|
||||||
/* Currently qc_prep_pkts() does not handle buffer wrapping so the
|
/* 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;
|
prv_pkt = cur_pkt;
|
||||||
}
|
}
|
||||||
else if (!(quic_tune.options & QUIC_TUNE_NO_UDP_GSO) &&
|
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 &&
|
dglen == qc->path->mtu &&
|
||||||
(char *)end < b_wrap(buf) &&
|
(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
|
/* A datagram covering the full MTU has been
|
||||||
* built, use GSO to built next entry. Do not
|
* built, use GSO to built next entry. Do not
|
||||||
* reserve extra space for datagram header.
|
* reserve extra space for datagram header.
|
||||||
|
17
src/server.c
17
src/server.c
@ -37,6 +37,7 @@
|
|||||||
#include <haproxy/protocol.h>
|
#include <haproxy/protocol.h>
|
||||||
#include <haproxy/proxy.h>
|
#include <haproxy/proxy.h>
|
||||||
#include <haproxy/queue.h>
|
#include <haproxy/queue.h>
|
||||||
|
#include <haproxy/quic_tp.h>
|
||||||
#include <haproxy/resolvers.h>
|
#include <haproxy/resolvers.h>
|
||||||
#include <haproxy/sample.h>
|
#include <haproxy/sample.h>
|
||||||
#include <haproxy/sc_strm.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_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) |
|
(parse_flags & SRV_PARSE_IN_PEER_SECTION ? PA_O_PORT_MAND : PA_O_PORT_OFS) |
|
||||||
PA_O_STREAM | PA_O_DGRAM | PA_O_XPRT);
|
PA_O_STREAM | PA_O_DGRAM | PA_O_XPRT);
|
||||||
|
|
||||||
if (!sk) {
|
if (!sk) {
|
||||||
ha_alert("%s\n", errmsg);
|
ha_alert("%s\n", errmsg);
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
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;
|
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 (!port1 || !port2) {
|
||||||
if (sk->ss_family != AF_CUST_RHTTP_SRV) {
|
if (sk->ss_family != AF_CUST_RHTTP_SRV) {
|
||||||
/* no port specified, +offset, -offset */
|
/* 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
|
* upper level is set as SF_ERR_NONE; -1 on failure, stream_err is set to
|
||||||
* appropriate value.
|
* 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 netns_entry *ns = NULL;
|
||||||
const struct protocol *proto;
|
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;
|
ns = __objt_server(conn->target)->netns;
|
||||||
}
|
}
|
||||||
#endif
|
#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);
|
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 */
|
/* at first, handle common to all proto families system limits and permission related errors */
|
||||||
if (sock_fd == -1) {
|
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;
|
fcount = ckchi->crtlist_entry->fcount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ckchi->is_server_instance)
|
if (ckchi->is_server_instance) {
|
||||||
errcode |= ckch_inst_new_load_srv_store(ckch_store->path, ckch_store, new_inst, err);
|
errcode |= ckch_inst_new_load_srv_store(ckch_store->path, ckch_store, new_inst, err, srv_is_quic(ckchi->server));
|
||||||
|
}
|
||||||
else
|
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);
|
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;
|
s = __objt_listener(conn->target)->bind_conf;
|
||||||
#ifdef USE_QUIC
|
#ifdef USE_QUIC
|
||||||
else if (qc)
|
else if (qc)
|
||||||
s = qc->li->bind_conf;
|
s = __objt_listener(qc->target)->bind_conf;
|
||||||
#endif /* USE_QUIC */
|
#endif /* USE_QUIC */
|
||||||
|
|
||||||
if (!s) {
|
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);
|
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
||||||
|
|
||||||
/* null if not a listener */
|
/* null if not a listener */
|
||||||
li = qc->li;
|
li = objt_listener(qc->target);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -65,6 +65,7 @@
|
|||||||
#include <haproxy/proxy.h>
|
#include <haproxy/proxy.h>
|
||||||
#include <haproxy/quic_conn.h>
|
#include <haproxy/quic_conn.h>
|
||||||
#include <haproxy/quic_openssl_compat.h>
|
#include <haproxy/quic_openssl_compat.h>
|
||||||
|
#include <haproxy/quic_ssl.h>
|
||||||
#include <haproxy/quic_tp.h>
|
#include <haproxy/quic_tp.h>
|
||||||
#include <haproxy/sample.h>
|
#include <haproxy/sample.h>
|
||||||
#include <haproxy/sc_strm.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;
|
ref = __objt_listener(conn->target)->bind_conf->keys_ref;
|
||||||
#ifdef USE_QUIC
|
#ifdef USE_QUIC
|
||||||
else if (qc)
|
else if (qc)
|
||||||
ref = qc->li->bind_conf->keys_ref;
|
ref = __objt_listener(qc->target)->bind_conf->keys_ref;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!ref) {
|
if (!ref) {
|
||||||
@ -1481,7 +1482,7 @@ int ssl_sock_bind_verifycbk(int ok, X509_STORE_CTX *x_store)
|
|||||||
else {
|
else {
|
||||||
qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
||||||
BUG_ON(!qc); /* Must never happen */
|
BUG_ON(!qc); /* Must never happen */
|
||||||
bind_conf = qc->li->bind_conf;
|
bind_conf = __objt_listener(qc->target)->bind_conf;
|
||||||
ctx = qc->xprt_ctx;
|
ctx = qc->xprt_ctx;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -3039,6 +3040,20 @@ error:
|
|||||||
return errcode;
|
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
|
* 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
|
* ERR_WARN if a warning is available into err
|
||||||
*/
|
*/
|
||||||
int ckch_inst_new_load_srv_store(const char *path, struct ckch_store *ckchs,
|
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;
|
SSL_CTX *ctx;
|
||||||
struct ckch_data *data;
|
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;
|
data = ckchs->data;
|
||||||
|
|
||||||
ctx = SSL_CTX_new(SSLv23_client_method());
|
ctx = ssl_sock_new_ssl_ctx(is_quic);
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
memprintf(err, "%sunable to allocate SSL context for cert '%s'.\n",
|
memprintf(err, "%sunable to allocate SSL context for cert '%s'.\n",
|
||||||
err && *err ? *err : "", path);
|
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;
|
int errcode = 0;
|
||||||
|
|
||||||
/* we found the ckchs in the tree, we can use it directly */
|
/* 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)
|
if (errcode & ERR_CODE)
|
||||||
return errcode;
|
return errcode;
|
||||||
@ -4398,7 +4414,9 @@ int ssl_sock_prepare_srv_ctx(struct server *srv)
|
|||||||
return cfgerr;
|
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;
|
srv->xprt = &ssl_sock;
|
||||||
|
|
||||||
if (srv->ssl_ctx.client_crt) {
|
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
|
/* The context will be uninitialized if there wasn't any "cert" option
|
||||||
* in the server line. */
|
* in the server line. */
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
ctx = SSL_CTX_new(SSLv23_client_method());
|
ctx = ssl_sock_new_ssl_ctx(srv_is_quic(srv));
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
ha_alert("unable to allocate ssl context.\n");
|
ha_alert("unable to allocate ssl context.\n");
|
||||||
cfgerr++;
|
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)
|
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);
|
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. */
|
/* Ensure thread connection migration is finalized ASAP. */
|
||||||
if (qc->flags & QUIC_FL_CONN_TID_REBIND)
|
if (qc->flags & QUIC_FL_CONN_TID_REBIND)
|
||||||
qc_finalize_tid_rebind(qc);
|
qc_finalize_tid_rebind(qc);
|
||||||
@ -128,7 +143,7 @@ static int qc_conn_init(struct connection *conn, void **xprt_ctx)
|
|||||||
out:
|
out:
|
||||||
TRACE_LEAVE(QUIC_EV_CONN_NEW, qc);
|
TRACE_LEAVE(QUIC_EV_CONN_NEW, qc);
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start the QUIC transport layer */
|
/* Start the QUIC transport layer */
|
||||||
@ -140,8 +155,13 @@ static int qc_xprt_start(struct connection *conn, void *ctx)
|
|||||||
qc = conn->handle.qc;
|
qc = conn->handle.qc;
|
||||||
TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
|
TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
|
||||||
|
|
||||||
|
if (objt_listener(conn->target)) {
|
||||||
/* mux-quic can now be considered ready. */
|
/* mux-quic can now be considered ready. */
|
||||||
qc->mux_state = QC_MUX_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
|
/* Schedule quic-conn to ensure post handshake frames are emitted. This
|
||||||
* is not done for 0-RTT as xprt->start happens before handshake
|
* 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,
|
.start = qc_xprt_start,
|
||||||
.prepare_bind_conf = ssl_sock_prepare_bind_conf,
|
.prepare_bind_conf = ssl_sock_prepare_bind_conf,
|
||||||
.destroy_bind_conf = ssl_sock_destroy_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_alpn = ssl_sock_get_alpn,
|
||||||
.get_ssl_sock_ctx = qc_get_ssl_sock_ctx,
|
.get_ssl_sock_ctx = qc_get_ssl_sock_ctx,
|
||||||
.dump_info = qc_xprt_dump_info,
|
.dump_info = qc_xprt_dump_info,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user