Compare commits

..

1 Commits

Author SHA1 Message Date
Remi Tricot-Le Breton
e04aba2fa3 MINOR: ssl: Add "renegotiate" server option
This "renegotiate" option can be set on SSL backends to allow secure
renegotiation. It is mostly useful with SSL libraries that disable
secure regotiation by default (such as BoringSSL or AWS-LC).
The "no-renegotiate" one can be used the other way around, to disable
secure renegotation that could be allowed by default.
Those two options can be set via "ssl-default-server-options" as well.
2025-06-11 09:18:40 +02:00
48 changed files with 470 additions and 1246 deletions

View File

@ -1,47 +1,6 @@
ChangeLog :
===========
2025/06/11 : 3.3-dev1
- BUILD: tools: properly define ha_dump_backtrace() to avoid a build warning
- DOC: config: Fix a typo in 2.7 (Name format for maps and ACLs)
- REGTESTS: Do not use REQUIRE_VERSION for HAProxy 2.5+ (5)
- REGTESTS: Remove REQUIRE_VERSION=2.3 from all tests
- REGTESTS: Remove REQUIRE_VERSION=2.4 from all tests
- REGTESTS: Remove tests with REQUIRE_VERSION_BELOW=2.4
- REGTESTS: Remove support for REQUIRE_VERSION and REQUIRE_VERSION_BELOW
- MINOR: server: group postinit server tasks under _srv_postparse()
- MINOR: stats: add stat_col flags
- MINOR: stats: add ME_NEW_COMMON() helper
- MINOR: proxy: collect per-capability stat in proxy_cond_disable()
- MINOR: proxy: add a true list containing all proxies
- MINOR: log: only run postcheck_log_backend() checks on backend
- MEDIUM: proxy: use global proxy list for REGISTER_POST_PROXY_CHECK() hook
- MEDIUM: server: automatically add server to proxy list in new_server()
- MEDIUM: server: add and use srv_init() function
- BUG/MAJOR: leastconn: Protect tree_elt with the lbprm lock
- BUG/MEDIUM: check: Requeue healthchecks on I/O events to handle check timeout
- CLEANUP: applet: Update comment for applet_put* functions
- DEBUG: check: Add the healthcheck's expiration date in the trace messags
- BUG/MINOR: mux-spop: Fix null-pointer deref on SPOP stream allocation failure
- CLEANUP: sink: remove useless cleanup in sink_new_from_logger()
- MAJOR: counters: add shared counters base infrastructure
- MINOR: counters: add shared counters helpers to get and drop shared pointers
- MINOR: counters: add common struct and flags to {fe,be}_counters_shared
- MEDIUM: counters: manage shared counters using dedicated helpers
- CLEANUP: counters: merge some common counters between {fe,be}_counters_shared
- MINOR: counters: add local-only internal rates to compute some maxes
- MAJOR: counters: dispatch counters over thread groups
- BUG/MEDIUM: cli: Properly parse empty lines and avoid crashed
- BUG/MINOR: config: emit warning for empty args only in discovery mode
- BUG/MINOR: config: fix arg number reported on empty arg warning
- BUG/MINOR: quic: Missing SSL session object freeing
- MINOR: applet: Add API functions to manipulate input and output buffers
- MINOR: applet: Add API functions to get data from the input buffer
- CLEANUP: applet: Simplify a bit comments for applet_put* functions
- MEDIUM: hlua: Update TCP applet functions to use the new applet API
- BUG/MEDIUM: fd: Use the provided tgid in fd_insert() to get tgroup_info
- BUG/MINIR: h1: Fix doc of 'accept-unsafe-...-request' about URI parsing
2025/05/28 : 3.3-dev0
- MINOR: version: mention that it's development again

View File

@ -1,2 +1,2 @@
$Format:%ci$
2025/06/11
2025/05/28

View File

@ -1 +1 @@
3.3-dev1
3.3-dev0

View File

@ -3,7 +3,7 @@
Configuration Manual
----------------------
version 3.3
2025/06/11
2025/05/28
This document covers the configuration language as implemented in the version
@ -9077,14 +9077,11 @@ no option accept-unsafe-violations-in-http-request
* In H1 only, NULL character in header value will be accepted;
* In H1 only, characters above 127 in the URI will be accepted. The list of
characters allowed to appear in a URI is well defined by RFC3986, and
chars 0-31, 32 (space), 34 ('"'), 60 ('<'), 62 ('>'), 92 ('\'), 94 ('^'),
96 ('`'), 123 ('{'), 124 ('|'), 125 ('}'), 127 (delete) and anything
above are normally not allowed. In H1, all character between (0..32) and
127 will always be blocked. All characters above 127 (excluded) will also
be blocked, except when this option is enabled. Other characters
(33..126) will not be checked at all.
* The list of characters allowed to appear in a URI is well defined by
RFC3986, and chars 0-31, 32 (space), 34 ('"'), 60 ('<'), 62 ('>'), 92
('\'), 94 ('^'), 96 ('`'), 123 ('{'), 124 ('|'), 125 ('}'), 127 (delete)
and anything above are normally not allowed. But here, in H1 only,
HAProxy will only block a number of them (0..32, 127);
* In H1 and H2, URLs containing fragment references ('#' after the path)
will be accepted;
@ -18010,6 +18007,21 @@ no-check-ssl
It may also be used as "default-server" setting to reset any previous
"default-server" "check-ssl" setting.
no-renegotiate
May be used in the following contexts: tcp, http, log
This setting is only available when support for OpenSSL was built in. It
disables the renegotiation mechanisms, be it the legacy unsafe one or the
more recent "secure renegotation" one (RFC 5746 TLS Renegotiation Indication
Extension) for the given SSL backend. This option is also available on global
statement "ssl-default-server-options".
Renegotiation is not posible anymore in TLS 1.3.
If neither "renegotiate" nor "no-renegotiate" is specified, the SSL library's
default behavior is kept.
Note that for instance OpenSSL library enables secure renegotiation by
default while BoringSSL and AWS-LC disable it.
See also "renegotiate".
no-send-proxy
May be used in the following contexts: tcp, http
@ -18338,6 +18350,21 @@ redir <prefix>
Example : server srv1 192.168.1.1:80 redir http://image1.mydomain.com check
renegotiate
May be used in the following contexts: tcp, http, log
This option enables the secure renegotiation mechanism (RFC 5746 TLS
Renegotiation Indication Extension) for a given SSL backend. It does not mean
that renegotiation requests will be sent by the SSL client, it only allows
backends to renegotiate when servers request it. It still requires that the
underlying SSL library actually supports renegotiation.
This option is also available on global statement "ssl-default-server-options".
Renegotiation is not posible anymore in TLS 1.3.
If neither "renegotiate" nor "no-renegotiate" is specified, the SSL library's
default behavior is kept.
Note that for instance OpenSSL library enables secure renegotiation by
default while BoringSSL and AWS-LC disable it.
rise <count>
May be used in the following contexts: tcp, http, log

View File

@ -282,92 +282,6 @@ static inline void applet_expect_data(struct appctx *appctx)
se_fl_clr(appctx->sedesc, SE_FL_EXP_NO_DATA);
}
/* Returns the buffer containing data pushed to the applet by the stream. For
* applets using its own buffers it is the appctx input buffer. For legacy
* applet, it is the output channel buffer.
*/
static inline struct buffer *applet_get_inbuf(struct appctx *appctx)
{
if (appctx->flags & APPCTX_FL_INOUT_BUFS)
return &appctx->inbuf;
else
return sc_ob(appctx_sc(appctx));
}
/* Returns the buffer containing data pushed by the applets to the stream. For
* applets using its own buffer it is the appctx output buffer. For legacy
* applet, it is the input channel buffer.
*/
static inline struct buffer *applet_get_outbuf(struct appctx *appctx)
{
if (appctx->flags & APPCTX_FL_INOUT_BUFS)
return &appctx->outbuf;
else
return sc_ib(appctx_sc(appctx));
}
/* Returns the amount of data in the input buffer (see applet_get_inbuf) */
static inline size_t applet_input_data(const struct appctx *appctx)
{
if (appctx->flags & APPCTX_FL_INOUT_BUFS)
return b_data(&appctx->inbuf);
else
return co_data(sc_oc(appctx_sc(appctx)));
}
/* Skips <len> bytes from the input buffer (see applet_get_inbuf).
*
* This is useful when data have been read directly from the buffer. It is
* illegal to call this function with <len> causing a wrapping at the end of the
* buffer. It's the caller's responsibility to ensure that <len> is never larger
* than available ouput data.
*/
static inline void applet_skip_input(struct appctx *appctx, size_t len)
{
if (appctx->flags & APPCTX_FL_INOUT_BUFS)
b_del(&appctx->inbuf, len);
else
co_skip(sc_oc(appctx_sc(appctx)), len);
}
/* Removes all bytes from the input buffer (see applet_get_inbuf).
*/
static inline void applet_reset_input(struct appctx *appctx)
{
if (appctx->flags & APPCTX_FL_INOUT_BUFS) {
b_reset(&appctx->inbuf);
applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
}
else
co_skip(sc_oc(appctx_sc(appctx)), co_data(sc_oc(appctx_sc(appctx))));
}
/* Returns the amout of space available at the output buffer (see applet_get_outbuf).
*/
static inline size_t applet_output_room(const struct appctx *appctx)
{
if (appctx->flags & APPCTX_FL_INOUT_BUFS)
return b_room(&appctx->outbuf);
else
return channel_recv_max(sc_ic(appctx_sc(appctx)));
}
/*Indicates that the applet have more data to deliver and it needs more room in
* the output buffer to do so (see applet_get_outbuf).
*
* For applets using its own buffers, <room_needed> is not used and only
* <appctx> flags are updated. For legacy applets, the amount of free space
* required must be specified. In this last case, it is the caller
* responsibility to be sure <room_needed> is valid.
*/
static inline void applet_need_room(struct appctx *appctx, size_t room_needed)
{
if (appctx->flags & APPCTX_FL_INOUT_BUFS)
applet_have_more_data(appctx);
else
sc_need_room(appctx_sc(appctx), room_needed);
}
/* Should only be used via wrappers applet_putchk() / applet_putchk_stress(). */
static inline int _applet_putchk(struct appctx *appctx, struct buffer *chunk,
int stress)
@ -404,7 +318,8 @@ static inline int _applet_putchk(struct appctx *appctx, struct buffer *chunk,
return ret;
}
/* writes chunk <chunk> into the applet output buffer (see applet_get_outbuf).
/* writes chunk <chunk> into the applet's output buffer if it uses its own
* bufferx or into the input channel of the stream attached to this applet.
*
* Returns the number of written bytes on success or -1 on error (lake of space,
* shutdown, invalid call...)
@ -420,7 +335,8 @@ static inline int applet_putchk_stress(struct appctx *appctx, struct buffer *chu
return _applet_putchk(appctx, chunk, 1);
}
/* writes <len> chars from <blk> into the applet output buffer (see applet_get_outbuf).
/* writes <len> chars from <blk> into the applet's output buffer if it uses its own
* bufferx or into the input channel of the stream attached to this applet.
*
* Returns the number of written bytes on success or -1 on error (lake of space,
* shutdown, invalid call...)
@ -455,8 +371,9 @@ static inline int applet_putblk(struct appctx *appctx, const char *blk, int len)
return ret;
}
/* writes chars from <str> up to the trailing zero (excluded) into the applet
* output buffer (see applet_get_outbuf).
/* writes chars from <str> up to the trailing zero (excluded) into the applet's
* output buffer if it uses its own bufferx or into the input channel of the
* stream attached to this applet.
*
* Returns the number of written bytes on success or -1 on error (lake of space,
* shutdown, invalid call...)
@ -492,7 +409,8 @@ static inline int applet_putstr(struct appctx *appctx, const char *str)
return ret;
}
/* writes character <chr> into the applet's output buffer (see applet_get_outbuf).
/* writes character <chr> into the applet's output buffer if it uses its own
* bufferx or into the input channel of the stream attached to this applet.
*
* Returns the number of written bytes on success or -1 on error (lake of space,
* shutdown, invalid call...)
@ -528,283 +446,6 @@ static inline int applet_putchr(struct appctx *appctx, char chr)
return ret;
}
static inline int applet_may_get(const struct appctx *appctx, size_t len)
{
if (appctx->flags & APPCTX_FL_INOUT_BUFS) {
if (len > b_data(&appctx->inbuf)) {
if (se_fl_test(appctx->sedesc, SE_FL_SHW))
return -1;
return 0;
}
}
else {
const struct stconn *sc = appctx_sc(appctx);
if ((sc->flags & SC_FL_SHUT_DONE) || len > co_data(sc_oc(sc))) {
if (sc->flags & (SC_FL_SHUT_DONE|SC_FL_SHUT_WANTED))
return -1;
return 0;
}
}
return 1;
}
/* Gets one char from the applet input buffer (see appet_get_inbuf),
*
* Return values :
* 1 : number of bytes read, equal to requested size.
* =0 : not enough data available. <c> is left undefined.
* <0 : no more bytes readable because output is shut.
*
* The status of the corresponding buffer is not changed. The caller must call
* applet_skip_input() to update it.
*/
static inline int applet_getchar(const struct appctx *appctx, char *c)
{
int ret;
ret = applet_may_get(appctx, 1);
if (ret <= 0)
return ret;
*c = ((appctx->flags & APPCTX_FL_INOUT_BUFS)
? *(b_head(&appctx->inbuf))
: *(co_head(sc_oc(appctx_sc(appctx)))));
return 1;
}
/* Copies one full block of data from the applet input buffer (see
* appet_get_inbuf).
*
* <len> bytes are capied, starting at the offset <offset>.
*
* Return values :
* >0 : number of bytes read, equal to requested size.
* =0 : not enough data available. <blk> is left undefined.
* <0 : no more bytes readable because output is shut.
*
* The status of the corresponding buffer is not changed. The caller must call
* applet_skip_input() to update it.
*/
static inline int applet_getblk(const struct appctx *appctx, char *blk, int len, int offset)
{
const struct buffer *buf;
int ret;
ret = applet_may_get(appctx, len+offset);
if (ret <= 0)
return ret;
buf = ((appctx->flags & APPCTX_FL_INOUT_BUFS)
? &appctx->inbuf
: sc_ob(appctx_sc(appctx)));
return b_getblk(buf, blk, len, offset);
}
/* Gets one text block representing a word from the applet input buffer (see
* appet_get_inbuf).
*
* The separator is waited for as long as some data can still be received and the
* destination is not full. Otherwise, the string may be returned as is, without
* the separator.
*
* Return values :
* >0 : number of bytes read. Includes the separator if present before len or end.
* =0 : no separator before end found. <str> is left undefined.
* <0 : no more bytes readable because output is shut.
*
* The status of the corresponding buffer is not changed. The caller must call
* applet_skip_input() to update it.
*/
static inline int applet_getword(const struct appctx *appctx, char *str, int len, char sep)
{
const struct buffer *buf;
char *p;
size_t input, max = len;
int ret = 0;
ret = applet_may_get(appctx, 1);
if (ret <= 0)
goto out;
if (appctx->flags & APPCTX_FL_INOUT_BUFS) {
buf = &appctx->inbuf;
input = b_data(buf);
}
else {
struct stconn *sc = appctx_sc(appctx);
buf = sc_ob(sc);
input = co_data(sc_oc(sc));
}
if (max > input) {
max = input;
str[max-1] = 0;
}
p = b_head(buf);
while (max) {
*str++ = *p;
ret++;
max--;
if (*p == sep)
goto out;
p = b_next(buf, p);
}
if (appctx->flags & APPCTX_FL_INOUT_BUFS) {
if (ret < len && (ret < input || b_room(buf)) &&
!se_fl_test(appctx->sedesc, SE_FL_SHW))
ret = 0;
}
else {
struct stconn *sc = appctx_sc(appctx);
if (ret < len && (ret < input || channel_may_recv(sc_oc(sc))) &&
!(sc->flags & (SC_FL_SHUT_DONE|SC_FL_SHUT_WANTED)))
ret = 0;
}
out:
if (max)
*str = 0;
return ret;
}
/* Gets one text block representing a line from the applet input buffer (see
* appet_get_inbuf).
*
* The '\n' is waited for as long as some data can still be received and the
* destination is not full. Otherwise, the string may be returned as is, without
* the '\n'.
*
* Return values :
* >0 : number of bytes read. Includes the \n if present before len or end.
* =0 : no '\n' before end found. <str> is left undefined.
* <0 : no more bytes readable because output is shut.
*
* The status of the corresponding buffer is not changed. The caller must call
* applet_skip_input() to update it.
*/
static inline int applet_getline(const struct appctx *appctx, char *str, int len)
{
return applet_getword(appctx, str, len, '\n');
}
/* Gets one or two blocks of data at once from the applet input buffer (see appet_get_inbuf),
*
* Data are not copied.
*
* Return values :
* >0 : number of blocks filled (1 or 2). blk1 is always filled before blk2.
* =0 : not enough data available. <blk*> are left undefined.
* <0 : no more bytes readable because output is shut.
*
* The status of the corresponding buffer is not changed. The caller must call
* applet_skip_input() to update it.
*/
static inline int applet_getblk_nc(const struct appctx *appctx, const char **blk1, size_t *len1, const char **blk2, size_t *len2)
{
const struct buffer *buf;
size_t max;
int ret;
ret = applet_may_get(appctx, 1);
if (ret <= 0)
return ret;
if (appctx->flags & APPCTX_FL_INOUT_BUFS) {
buf = &appctx->inbuf;
max = b_data(buf);
}
else {
struct stconn *sc = appctx_sc(appctx);
buf = sc_ob(sc);
max = co_data(sc_oc(sc));
}
return b_getblk_nc(buf, blk1, len1, blk2, len2, 0, max);
}
/* Gets one or two blocks of text representing a word from the applet input
* buffer (see appet_get_inbuf).
*
* Data are not copied. The separator is waited for as long as some data can
* still be received and the destination is not full. Otherwise, the string may
* be returned as is, without the separator.
*
* Return values :
* >0 : number of bytes read. Includes the separator if present before len or end.
* =0 : no separator before end found. <str> is left undefined.
* <0 : no more bytes readable because output is shut.
*
* The status of the corresponding buffer is not changed. The caller must call
* applet_skip_input() to update it.
*/
static inline int applet_getword_nc(const struct appctx *appctx, const char **blk1, size_t *len1, const char **blk2, size_t *len2, char sep)
{
int ret;
size_t l;
ret = applet_getblk_nc(appctx, blk1, len1, blk2, len2);
if (unlikely(ret <= 0))
return ret;
for (l = 0; l < *len1 && (*blk1)[l] != sep; l++);
if (l < *len1 && (*blk1)[l] == sep) {
*len1 = l + 1;
return 1;
}
if (ret >= 2) {
for (l = 0; l < *len2 && (*blk2)[l] != sep; l++);
if (l < *len2 && (*blk2)[l] == sep) {
*len2 = l + 1;
return 2;
}
}
/* If we have found no LF and the buffer is full or the SC is shut, then
* the resulting string is made of the concatenation of the pending
* blocks (1 or 2).
*/
if (appctx->flags & APPCTX_FL_INOUT_BUFS) {
if (b_full(&appctx->inbuf) || se_fl_test(appctx->sedesc, SE_FL_SHW))
return ret;
}
else {
struct stconn *sc = appctx_sc(appctx);
if (!channel_may_recv(sc_oc(sc)) || sc->flags & (SC_FL_SHUT_DONE|SC_FL_SHUT_WANTED))
return ret;
}
/* No LF yet and not shut yet */
return 0;
}
/* Gets one or two blocks of text representing a line from the applet input
* buffer (see appet_get_inbuf).
*
* Data are not copied. The '\n' is waited for as long as some data can still be
* received and the destination is not full. Otherwise, the string may be
* returned as is, without the '\n'.
*
* Return values :
* >0 : number of bytes read. Includes the \n if present before len or end.
* =0 : no '\n' before end found. <str> is left undefined.
* <0 : no more bytes readable because output is shut.
*
* The status of the corresponding buffer is not changed. The caller must call
* applet_skip_input() to update it.
*/
static inline int applet_getline_nc(const struct appctx *appctx, const char **blk1, size_t *len1, const char **blk2, size_t *len2)
{
return applet_getword_nc(appctx, blk1, len1, blk2, len2, '\n');
}
#endif /* _HAPROXY_APPLET_H */
/*

View File

@ -499,7 +499,6 @@ static inline long fd_clr_running(int fd)
static inline void fd_insert(int fd, void *owner, void (*iocb)(int fd), int tgid, unsigned long thread_mask)
{
extern void sock_conn_iocb(int);
struct tgroup_info *tginfo = &ha_tgroup_info[tgid - 1];
int newstate;
/* conn_fd_handler should support edge-triggered FDs */
@ -529,7 +528,7 @@ static inline void fd_insert(int fd, void *owner, void (*iocb)(int fd), int tgid
BUG_ON(fdtab[fd].state != 0);
BUG_ON(tgid < 1 || tgid > MAX_TGROUPS);
thread_mask &= tginfo->threads_enabled;
thread_mask &= tg->threads_enabled;
BUG_ON(thread_mask == 0);
fd_claim_tgid(fd, tgid);

View File

@ -202,7 +202,7 @@ struct qcc_app_ops {
/* Initialize <qcs> stream app context or leave it to NULL if rejected. */
int (*attach)(struct qcs *qcs, void *conn_ctx);
/* Convert received HTTP payload to HTX. Returns amount of decoded bytes from <b> or a negative error code. */
/* Convert received HTTP payload to HTX. */
ssize_t (*rcv_buf)(struct qcs *qcs, struct buffer *b, int fin);
/* Convert HTX to HTTP payload for sending. */
@ -232,7 +232,7 @@ struct qcc_app_ops {
#define QC_CF_ERRL 0x00000001 /* fatal error detected locally, connection should be closed soon */
#define QC_CF_ERRL_DONE 0x00000002 /* local error properly handled, connection can be released */
#define QC_CF_IS_BACK 0x00000004 /* backend side */
/* unused 0x00000004 */
#define QC_CF_CONN_FULL 0x00000008 /* no stream buffers available on connection */
/* unused 0x00000010 */
#define QC_CF_ERR_CONN 0x00000020 /* fatal error reported by transport layer */

View File

@ -228,9 +228,6 @@ struct quic_version {
extern const struct quic_version quic_versions[];
extern const size_t quic_versions_nb;
extern const struct quic_version *preferred_version;
extern const struct quic_version *quic_version_draft_29;
extern const struct quic_version *quic_version_1;
extern const struct quic_version *quic_version_2;
/* unused: 0x01 */
/* Flag the packet number space as requiring an ACK frame to be sent. */
@ -308,7 +305,6 @@ struct qcc_app_ops;
/* Number of received bytes. */ \
uint64_t rx; \
} bytes; \
size_t max_udp_payload; \
/* First DCID used by client on its Initial packet. */ \
struct quic_cid odcid; \
/* DCID of our endpoint - not updated when a new DCID is used */ \
@ -319,7 +315,7 @@ struct qcc_app_ops;
* with a connection \
*/ \
struct eb_root *cids; \
enum obj_type *target; \
struct listener *li; /* only valid for frontend connections */ \
/* Idle timer task */ \
struct task *idle_timer_task; \
unsigned int idle_expire; \
@ -460,7 +456,6 @@ struct quic_conn_closed {
#define QUIC_FL_CONN_HPKTNS_DCD (1U << 16) /* Handshake packet number space discarded */
#define QUIC_FL_CONN_PEER_VALIDATED_ADDR (1U << 17) /* Peer address is considered as validated for this connection. */
#define QUIC_FL_CONN_NO_TOKEN_RCVD (1U << 18) /* Client dit not send any token */
#define QUIC_FL_CONN_SCID_RECEIVED (1U << 19) /* (client only: first Initial received. */
/* gap here */
#define QUIC_FL_CONN_TO_KILL (1U << 24) /* Unusable connection, to be killed */
#define QUIC_FL_CONN_TX_TP_RECEIVED (1U << 25) /* Peer transport parameters have been received (used for the transmitting part) */

View File

@ -69,8 +69,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
struct quic_connection_id *conn_id,
struct sockaddr_storage *local_addr,
struct sockaddr_storage *peer_addr,
int token, void *owner,
struct connection *conn);
int server, int token, void *owner);
int quic_build_post_handshake_frames(struct quic_conn *qc);
const struct quic_version *qc_supported_version(uint32_t version);
int quic_peer_validated_addr(struct quic_conn *qc);
@ -164,22 +163,6 @@ static inline void quic_free_ncbuf(struct ncbuf *ncbuf)
*ncbuf = NCBUF_NULL;
}
/* Return the address of the QUIC counters attached to the proxy of
* the owner of the connection whose object type address is <o> for
* listener and servers, or NULL for others object type.
*/
static inline void *qc_counters(enum obj_type *o, const struct stats_module *m)
{
struct proxy *p;
struct listener *l = objt_listener(o);
struct server *s = objt_server(o);
p = l ? l->bind_conf->frontend :
s ? s->proxy : NULL;
return p ? EXTRA_COUNTERS_GET(p->extra_counters_fe, m) : NULL;
}
void chunk_frm_appendf(struct buffer *buf, const struct quic_frame *frm);
void quic_set_connection_close(struct quic_conn *qc, const struct quic_err err);
void quic_set_tls_alert(struct quic_conn *qc, int alert);

View File

@ -26,7 +26,7 @@
#include <haproxy/quic_rx-t.h>
int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
enum obj_type *obj_type);
struct listener *li);
int qc_treat_rx_pkts(struct quic_conn *qc);
int qc_parse_hd_form(struct quic_rx_packet *pkt,
unsigned char **pos, const unsigned char *end);

View File

@ -33,7 +33,6 @@
#include <haproxy/connection-t.h>
#include <haproxy/fd-t.h>
#include <haproxy/listener-t.h>
#include <haproxy/obj_type.h>
#include <haproxy/quic_conn-t.h>
#include <haproxy/quic_sock-t.h>
@ -79,8 +78,7 @@ static inline char qc_test_fd(struct quic_conn *qc)
*/
static inline int qc_fd(struct quic_conn *qc)
{
/* TODO: check this: For backends, qc->fd is always initialized */
return qc_test_fd(qc) ? qc->fd : __objt_listener(qc->target)->rx.fd;
return qc_test_fd(qc) ? qc->fd : qc->li->rx.fd;
}
/* Try to increment <l> handshake current counter. If listener limit is

View File

@ -34,8 +34,7 @@
#include <haproxy/ssl_sock-t.h>
int ssl_quic_initial_ctx(struct bind_conf *bind_conf);
SSL_CTX *ssl_quic_srv_new_ssl_ctx(void);
int qc_alloc_ssl_sock_ctx(struct quic_conn *qc, struct connection *conn);
int qc_alloc_ssl_sock_ctx(struct quic_conn *qc);
int qc_ssl_provide_all_quic_data(struct quic_conn *qc, struct ssl_sock_ctx *ctx);
int quic_ssl_set_tls_cbs(SSL *ssl);

View File

@ -26,9 +26,6 @@ int qc_lstnr_params_init(struct quic_conn *qc,
const unsigned char *dcid, size_t dcidlen,
const unsigned char *scid, size_t scidlen,
const struct quic_cid *token_odcid);
void qc_srv_params_init(struct quic_conn *qc,
const struct quic_transport_params *srv_params,
const unsigned char *scid, size_t scidlen);
/* Dump <cid> transport parameter connection ID value if present (non null length).
* Used only for debugging purposes.

View File

@ -304,6 +304,13 @@ struct srv_pp_tlv_list {
unsigned char type;
};
/* Renegotiate mode */
enum renegotiate_mode {
SSL_RENEGOTIATE_DFLT = 0, /* Use the SSL library's default behavior */
SSL_RENEGOTIATE_OFF, /* Disable secure renegotiation */
SSL_RENEGOTIATE_ON /* Enable secure renegotiation */
};
struct proxy;
struct server {
/* mostly config or admin stuff, doesn't change often */
@ -477,10 +484,8 @@ struct server {
int npn_len; /* NPN protocol string length */
char *alpn_str; /* ALPN protocol string */
int alpn_len; /* ALPN protocol string length */
int renegotiate; /* Renegotiate mode (SSL_RENEGOTIATE_ flag) */
} ssl_ctx;
#ifdef USE_QUIC
struct quic_transport_params quic_params; /* QUIC transport parameters */
#endif
struct resolv_srvrq *srvrq; /* Pointer representing the DNS SRV requeest, if any */
struct list srv_rec_item; /* to attach server to a srv record item */
struct list ip_rec_item; /* to attach server to a A or AAAA record item */

View File

@ -343,16 +343,6 @@ 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 */
/*

View File

@ -28,11 +28,9 @@
#include <haproxy/api.h>
#include <haproxy/connection-t.h>
#include <haproxy/listener-t.h>
#include <haproxy/protocol-t.h>
#include <haproxy/sock-t.h>
int sock_create_server_socket(struct connection *conn, struct proxy *be,
enum proto_type proto_type, int sock_type, int *stream_err);
int sock_create_server_socket(struct connection *conn, struct proxy *be, int *stream_err);
void sock_enable(struct receiver *rx);
void sock_disable(struct receiver *rx);
void sock_unbind(struct receiver *rx);

View File

@ -62,7 +62,7 @@ struct ckch_inst *ckch_inst_new();
int ckch_inst_new_load_store(const char *path, struct ckch_store *ckchs, struct bind_conf *bind_conf,
struct ssl_bind_conf *ssl_conf, char **sni_filter, int fcount, int is_default, struct ckch_inst **ckchi, char **err);
int ckch_inst_new_load_srv_store(const char *path, struct ckch_store *ckchs,
struct ckch_inst **ckchi, char **err, int is_quic);
struct ckch_inst **ckchi, char **err);
int ckch_inst_rebuild(struct ckch_store *ckch_store, struct ckch_inst *ckchi,
struct ckch_inst **new_inst, char **err);

View File

@ -320,6 +320,8 @@ struct global_ssl {
#ifdef HAVE_ACME
int acme_scheduler;
#endif
int renegotiate; /* Renegotiate mode (SSL_RENEGOTIATE_ flag) */
};
/* The order here matters for picking a default context,

View File

@ -1992,9 +1992,7 @@ int connect_server(struct stream *s)
/* set the correct protocol on the output stream connector */
if (srv) {
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)) {
if (conn_prepare(srv_conn, protocol_lookup(srv_conn->dst->ss_family, PROTO_TYPE_STREAM, srv->alt_proto), srv->xprt)) {
conn_free(srv_conn);
return SF_ERR_INTERNAL;
}
@ -2020,13 +2018,9 @@ int connect_server(struct stream *s)
srv_conn->ctx = s->scb;
#if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
/* Delay mux initialization if SSL and ALPN/NPN is set. Note
* that this is skipped in TCP mode as we only want mux-pt
* anyway.
*/
if (!srv ||
(srv->use_ssl != 1 || (!(srv->ssl_ctx.alpn_str) && !(srv->ssl_ctx.npn_str)) ||
!IS_HTX_STRM(s)))
srv->mux_proto || !IS_HTX_STRM(s)))
#endif
init_mux = 1;

View File

@ -1630,6 +1630,19 @@ static int srv_parse_check_sni(char **args, int *cur_arg, struct proxy *px, stru
}
/* parse the "renegotiate" server keyword */
static int srv_parse_renegotiate(char **args, int *cur_arg, struct proxy *px,
struct server *newsrv, char **err)
{
if (strncmp(*args, "no-", 3) == 0)
newsrv->ssl_ctx.renegotiate = SSL_RENEGOTIATE_OFF;
else
newsrv->ssl_ctx.renegotiate = SSL_RENEGOTIATE_ON;
return 0;
}
/* common function to init ssl_ctx */
static int ssl_sock_init_srv(struct server *s)
{
@ -1675,6 +1688,9 @@ static int ssl_sock_init_srv(struct server *s)
}
#endif
if (global_ssl.renegotiate && !s->ssl_ctx.renegotiate)
s->ssl_ctx.renegotiate = global_ssl.renegotiate;
return 0;
}
@ -2079,6 +2095,9 @@ static int ssl_parse_default_server_options(char **args, int section_type, struc
return -1;
}
}
else if (strcmp(args[i], "renegotiate") == 0 || strcmp(args[i], "no-renegotiate") == 0) {
global_ssl.renegotiate = (*args[i] == 'n') ? SSL_RENEGOTIATE_OFF : SSL_RENEGOTIATE_ON;
}
else if (parse_tls_method_options(args[i], &global_ssl.connect_default_sslmethods, err)) {
memprintf(err, "unknown option '%s' on global statement '%s'.", args[i], args[0]);
return -1;
@ -2505,6 +2524,7 @@ static struct srv_kw_list srv_kws = { "SSL", { }, {
{ "force-tlsv12", srv_parse_tls_method_options, 0, 1, 1 }, /* force TLSv12 */
{ "force-tlsv13", srv_parse_tls_method_options, 0, 1, 1 }, /* force TLSv13 */
{ "no-check-ssl", srv_parse_no_check_ssl, 0, 1, 0 }, /* disable SSL for health checks */
{ "no-renegotiate", srv_parse_renegotiate, 0, 1, 1 }, /* Disable renegotiation */
{ "no-send-proxy-v2-ssl", srv_parse_no_send_proxy_ssl, 0, 1, 0 }, /* do not send PROXY protocol header v2 with SSL info */
{ "no-send-proxy-v2-ssl-cn", srv_parse_no_send_proxy_cn, 0, 1, 0 }, /* do not send PROXY protocol header v2 with CN */
{ "no-ssl", srv_parse_no_ssl, 0, 1, 0 }, /* disable SSL processing */
@ -2516,6 +2536,7 @@ static struct srv_kw_list srv_kws = { "SSL", { }, {
{ "no-tlsv13", srv_parse_tls_method_options, 0, 0, 1 }, /* disable TLSv13 */
{ "no-tls-tickets", srv_parse_no_tls_tickets, 0, 1, 1 }, /* disable session resumption tickets */
{ "npn", srv_parse_npn, 1, 1, 1 }, /* Set NPN supported protocols */
{ "renegotiate", srv_parse_renegotiate, 0, 1, 1 }, /* Allow secure renegotiation */
{ "send-proxy-v2-ssl", srv_parse_send_proxy_ssl, 0, 1, 1 }, /* send PROXY protocol header v2 with SSL info */
{ "send-proxy-v2-ssl-cn", srv_parse_send_proxy_cn, 0, 1, 1 }, /* send PROXY protocol header v2 with CN */
{ "sigalgs", srv_parse_sigalgs, 1, 1, 1 }, /* signature algorithms */

View File

@ -3767,8 +3767,6 @@ out_uri_auth_compat:
((newsrv->flags & SRV_F_DEFSRV_USE_SSL) && newsrv->use_ssl != 1)) {
if (xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->prepare_srv)
cfgerr += xprt_get(XPRT_SSL)->prepare_srv(newsrv);
else if (xprt_get(XPRT_QUIC) && xprt_get(XPRT_QUIC)->prepare_srv)
cfgerr += xprt_get(XPRT_QUIC)->prepare_srv(newsrv);
}
if ((newsrv->flags & SRV_F_FASTOPEN) &&
@ -4115,13 +4113,6 @@ out_uri_auth_compat:
int mode = conn_pr_mode_to_proto_mode(curproxy->mode);
const struct mux_proto_list *mux_ent;
if (srv_is_quic(newsrv)) {
if (!newsrv->mux_proto) {
/* Force QUIC as mux-proto on server with quic addresses, similarly to bind on FE side. */
newsrv->mux_proto = get_mux_proto(ist("quic"));
}
}
if (!newsrv->mux_proto)
continue;

View File

@ -1423,8 +1423,7 @@ static int cli_io_handler_show_fd(struct appctx *appctx)
#if defined(USE_QUIC)
else if (fdt.iocb == quic_conn_sock_fd_iocb) {
qc = fdtab[fd].owner;
li = qc ? objt_listener(qc->target) : NULL;
sv = qc ? objt_server(qc->target) : NULL;
li = qc ? qc->li : NULL;
xprt_ctx = qc ? qc->xprt_ctx : NULL;
conn = qc ? qc->conn : NULL;
xprt = conn ? conn->xprt : NULL; // in fact it's &ssl_quic

View File

@ -36,7 +36,6 @@
#include <haproxy/qmux_http.h>
#include <haproxy/qpack-dec.h>
#include <haproxy/qpack-enc.h>
#include <haproxy/quic_conn.h>
#include <haproxy/quic_enc.h>
#include <haproxy/quic_fctl.h>
#include <haproxy/quic_frame.h>
@ -1385,13 +1384,9 @@ static ssize_t h3_parse_settings_frm(struct h3c *h3c, const struct buffer *buf,
/* Transcode HTTP/3 payload received in buffer <b> to HTX data for stream
* <qcs>. If <fin> is set, it indicates that no more data will arrive after.
*
* It may be necessary to call this function with an empty input buffer to
* signal a standalone FIN.
*
* Returns the amount of decoded bytes from <b> or a negative error code. A
* null value can be returned even if input buffer is not empty, meaning that
* transcoding cannot be performed due to partial HTTP/3 content. Receive
* operation may be called later with newer data available.
* Returns the count of consumed bytes or a negative error code. If 0 is
* returned, stream data is incomplete, decoding should be call again later
* with more content.
*/
static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
{
@ -1691,13 +1686,6 @@ static int h3_encode_header(struct buffer *buf,
return qpack_encode_header(buf, n, v_strip);
}
/* Convert a HTX status-line and associated headers stored into <htx> into a
* HTTP/3 HEADERS response frame. HTX blocks are removed up to end-of-trailer
* included.
*
* Returns the amount of consumed bytes from <htx> buffer or a negative error
* code.
*/
static int h3_resp_headers_send(struct qcs *qcs, struct htx *htx)
{
int err;
@ -1852,15 +1840,15 @@ static int h3_resp_headers_send(struct qcs *qcs, struct htx *htx)
}
/* Convert a series of HTX trailer blocks from <htx> buffer into <qcs> buffer
* as a HTTP/3 HEADERS frame. Forbidden trailers are skipped. HTX trailer
* blocks are removed from <htx> up to end-of-trailer included.
* as a H3 HEADERS frame. H3 forbidden trailers are skipped. HTX trailer blocks
* are removed from <htx> until EOT is found and itself removed.
*
* If only a EOT HTX block is present without trailer, no H3 frame is produced.
* Caller is responsible to emit an empty QUIC STREAM frame to signal the end
* of the stream.
*
* Returns the amount of consumed bytes from <htx> buffer or a negative error
* code.
* Returns the size of HTX blocks removed. A negative error code is returned in
* case of a fatal error which should caused a connection closure.
*/
static int h3_resp_trailers_send(struct qcs *qcs, struct htx *htx)
{
@ -2032,9 +2020,9 @@ static int h3_resp_trailers_send(struct qcs *qcs, struct htx *htx)
* underlying HTX area via <buf> as this will be used if zero-copy can be
* performed.
*
* Returns the amount of consumed bytes from <htx> buffer, which corresponds to
* the length sum of encoded frames payload. A negative error code is returned
* in case of a fatal error which should caused a connection closure.
* Returns the total bytes of encoded HTTP/3 payload. This corresponds to the
* total bytes of HTX block removed. A negative error code is returned in case
* of a fatal error which should caused a connection closure.
*/
static int h3_resp_data_send(struct qcs *qcs, struct htx *htx,
struct buffer *buf, size_t count)
@ -2159,14 +2147,6 @@ static int h3_resp_data_send(struct qcs *qcs, struct htx *htx,
return -1;
}
/* Transcode HTX data from <buf> of length <count> into HTTP/3 frame for <qcs>
* stream instance. Successfully transcoded HTX blocks are removed from input
* buffer.
*
* Returns the amount of consumed bytes from <buf>. In case of error,
* connection is flagged and transcoding is interrupted. The returned value is
* unchanged though.
*/
static size_t h3_snd_buf(struct qcs *qcs, struct buffer *buf, size_t count)
{
size_t total = 0;
@ -2533,6 +2513,7 @@ static int h3_send_goaway(struct h3c *h3c)
static int h3_init(struct qcc *qcc)
{
struct h3c *h3c;
const struct listener *li = __objt_listener(qcc->conn->target);
TRACE_ENTER(H3_EV_H3C_NEW, qcc->conn);
@ -2549,7 +2530,9 @@ static int h3_init(struct qcc *qcc)
h3c->id_goaway = 0;
qcc->ctx = h3c;
h3c->prx_counters = qc_counters(qcc->conn->target, &h3_stats_module);
h3c->prx_counters =
EXTRA_COUNTERS_GET(li->bind_conf->frontend->extra_counters_fe,
&h3_stats_module);
LIST_INIT(&h3c->buf_wait.list);
TRACE_LEAVE(H3_EV_H3C_NEW, qcc->conn);

View File

@ -5299,20 +5299,48 @@ __LJMP static int hlua_applet_tcp_get_priv(lua_State *L)
__LJMP static int hlua_applet_tcp_getline_yield(lua_State *L, int status, lua_KContext ctx)
{
struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
struct stconn *sc = appctx_sc(luactx->appctx);
int ret;
const char *blk1 = NULL;
size_t len1 = 0;
const char *blk2 = NULL;
size_t len2 = 0;
const char *blk1;
size_t len1;
const char *blk2;
size_t len2;
/* Read the maximum amount of data available. */
ret = applet_getline_nc(luactx->appctx, &blk1, &len1, &blk2, &len2);
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS) {
size_t l;
int line_found = 0;
ret = b_getblk_nc(&luactx->appctx->inbuf, &blk1, &len1, &blk2, &len2, 0, b_data(&luactx->appctx->inbuf));
if (ret == 0 && se_fl_test(luactx->appctx->sedesc, SE_FL_SHW))
ret = -1;
if (ret >= 1) {
for (l = 0; l < len1 && blk1[l] != '\n'; l++);
if (l < len1 && blk1[l] == '\n') {
len1 = l + 1;
line_found = 1;
}
}
if (!line_found && ret >= 2) {
for (l = 0; l < len2 && blk2[l] != '\n'; l++);
if (l < len2 && blk2[l] == '\n') {
len2 = l + 1;
line_found = 1;
}
}
if (!line_found && !se_fl_test(luactx->appctx->sedesc, SE_FL_SHW))
ret = 0;
}
else
ret = co_getline_nc(sc_oc(sc), &blk1, &len1, &blk2, &len2);
/* Data not yet available. return yield. */
if (ret == 0) {
applet_need_more_data(luactx->appctx);
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_tcp_getline_yield, TICK_ETERNITY, 0));
return 0;
}
/* End of data: commit the total strings and return. */
@ -5327,10 +5355,13 @@ __LJMP static int hlua_applet_tcp_getline_yield(lua_State *L, int status, lua_KC
/* don't check the max length read and don't check. */
luaL_addlstring(&luactx->b, blk1, len1);
if (len2)
luaL_addlstring(&luactx->b, blk2, len2);
luaL_addlstring(&luactx->b, blk2, len2);
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS)
b_del(&luactx->appctx->inbuf, len1 + len2);
else
co_skip(sc_oc(sc), len1 + len2);
applet_skip_input(luactx->appctx, len1+len2);
luaL_pushresult(&luactx->b);
return 1;
}
@ -5352,16 +5383,23 @@ __LJMP static int hlua_applet_tcp_getline(lua_State *L)
__LJMP static int hlua_applet_tcp_recv_try(lua_State *L)
{
struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
struct stconn *sc = appctx_sc(luactx->appctx);
size_t len = MAY_LJMP(luaL_checkinteger(L, 2));
int exp_date = MAY_LJMP(luaL_checkinteger(L, 3));
int ret;
const char *blk1 = NULL;
size_t len1 = 0;
const char *blk2 = NULL;
size_t len2 = 0;
const char *blk1;
size_t len1;
const char *blk2;
size_t len2;
/* Read the maximum amount of data available. */
ret = applet_getblk_nc(luactx->appctx, &blk1, &len1, &blk2, &len2);
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS) {
ret = b_getblk_nc(&luactx->appctx->inbuf, &blk1, &len1, &blk2, &len2, 0, b_data(&luactx->appctx->inbuf));
if (ret == 0 && se_fl_test(luactx->appctx->sedesc, SE_FL_SHW))
ret = -1;
}
else
ret = co_getblk_nc(sc_oc(sc), &blk1, &len1, &blk2, &len2);
/* Data not yet available. return yield. */
if (ret == 0) {
@ -5392,9 +5430,11 @@ __LJMP static int hlua_applet_tcp_recv_try(lua_State *L)
* the end of data stream.
*/
luaL_addlstring(&luactx->b, blk1, len1);
if (len2)
luaL_addlstring(&luactx->b, blk2, len2);
applet_skip_input(luactx->appctx, len1+len2);
luaL_addlstring(&luactx->b, blk2, len2);
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS)
b_del(&luactx->appctx->inbuf, len1 + len2);
else
co_skip(sc_oc(sc), len1 + len2);
if (tick_is_expired(exp_date, now_ms)) {
/* return the result. */
@ -5414,14 +5454,15 @@ __LJMP static int hlua_applet_tcp_recv_try(lua_State *L)
len -= len1;
/* Copy the second block. */
if (len2) {
if (len2 > len)
len2 = len;
luaL_addlstring(&luactx->b, blk2, len2);
len -= len2;
}
if (len2 > len)
len2 = len;
luaL_addlstring(&luactx->b, blk2, len2);
len -= len2;
applet_skip_input(luactx->appctx, len1+len2);
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS)
b_del(&luactx->appctx->inbuf, len1 + len2);
else
co_skip(sc_oc(sc), len1 + len2);
/* If there is no other data available, yield waiting for new data. */
if (len > 0 && !tick_is_expired(exp_date, now_ms)) {
@ -5530,10 +5571,15 @@ __LJMP static int hlua_applet_tcp_send_yield(lua_State *L, int status, lua_KCont
struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
const char *str = MAY_LJMP(luaL_checklstring(L, 2, &len));
int l = MAY_LJMP(luaL_checkinteger(L, 3));
struct stconn *sc = appctx_sc(luactx->appctx);
struct channel *chn = sc_ic(sc);
int max;
/* Get the max amount of data which can be written */
max = applet_output_room(luactx->appctx);
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS)
max = b_room(&luactx->appctx->outbuf);
else
max = channel_recv_max(chn);
if (max > (len - l))
max = len - l;
@ -5550,7 +5596,10 @@ __LJMP static int hlua_applet_tcp_send_yield(lua_State *L, int status, lua_KCont
* applet, and returns a yield.
*/
if (l < len) {
applet_need_room(luactx->appctx, applet_output_room(luactx->appctx) + 1);
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS)
applet_have_more_data(luactx->appctx);
else
sc_need_room(sc, channel_recv_max(chn) + 1);
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_tcp_send_yield, TICK_ETERNITY, 0));
}
@ -11212,7 +11261,10 @@ out:
if (yield)
return;
applet_reset_input(ctx);
if (ctx->flags & APPCTX_FL_INOUT_BUFS)
b_reset(&ctx->inbuf);
else
co_skip(sc_oc(sc), co_data(sc_oc(sc)));
return;
error:

View File

@ -6,15 +6,13 @@
#include <haproxy/dynbuf.h>
#include <haproxy/htx.h>
#include <haproxy/http.h>
#include <haproxy/istbuf.h>
#include <haproxy/mux_quic.h>
#include <haproxy/qmux_http.h>
#include <haproxy/qmux_trace.h>
#include <haproxy/quic_utils.h>
#include <haproxy/trace.h>
/* HTTP/0.9 request -> HTX. */
static ssize_t hq_interop_rcv_buf_req(struct qcs *qcs, struct buffer *b, int fin)
static ssize_t hq_interop_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
{
struct htx *htx;
struct htx_sl *sl;
@ -94,74 +92,12 @@ static ssize_t hq_interop_rcv_buf_req(struct qcs *qcs, struct buffer *b, int fin
return b_data(b);
}
/* HTTP/0.9 response -> HTX. */
static ssize_t hq_interop_rcv_buf_res(struct qcs *qcs, struct buffer *b, int fin)
{
ssize_t ret = 0;
struct htx *htx;
struct htx_sl *sl;
struct buffer *htx_buf;
const struct stream *strm = __sc_strm(qcs->sd->sc);
const unsigned int flags = HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN;
size_t htx_sent;
htx_buf = qcc_get_stream_rxbuf(qcs);
BUG_ON(!htx_buf);
htx = htx_from_buf(htx_buf);
if (htx_is_empty(htx) && !strm->res.total) {
/* First data transfer, add HTX response start-line first. */
sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags,
ist("HTTP/1.0"), ist("200"), ist(""));
BUG_ON(!sl);
if (fin && !b_data(b))
sl->flags |= HTX_SL_F_BODYLESS;
htx_add_endof(htx, HTX_BLK_EOH);
}
if (!b_data(b)) {
if (fin && quic_stream_is_bidi(qcs->id)) {
if (qcs_http_handle_standalone_fin(qcs)) {
htx_to_buf(htx, htx_buf);
return -1;
}
}
}
else {
BUG_ON(b_data(b) > htx_free_data_space(htx)); /* TODO */
BUG_ON(b_head(b) + b_data(b) > b_wrap(b)); /* TODO */
htx_sent = htx_add_data(htx, ist2(b_head(b), b_data(b)));
BUG_ON(htx_sent < b_data(b)); /* TODO */
ret = htx_sent;
if (fin && b_data(b) == htx_sent)
htx->flags |= HTX_FL_EOM;
}
htx_to_buf(htx, htx_buf);
return ret;
}
/* Returns the amount of decoded bytes from <b> or a negative error code. */
static ssize_t hq_interop_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
{
/* hq-interop parser does not support buffer wrapping. */
BUG_ON(b_data(b) != b_contig_data(b, 0));
return !(qcs->qcc->flags & QC_CF_IS_BACK) ?
hq_interop_rcv_buf_req(qcs, b, fin) :
hq_interop_rcv_buf_res(qcs, b, fin);
}
/* Returns the amount of consumed bytes from <buf>. */
static size_t hq_interop_snd_buf(struct qcs *qcs, struct buffer *buf,
size_t count)
{
enum htx_blk_type btype;
struct htx *htx = NULL;
struct htx_blk *blk;
struct htx_sl *sl = NULL;
int32_t idx;
uint32_t bsize, fsize;
struct buffer *res = NULL;
@ -177,25 +113,9 @@ static size_t hq_interop_snd_buf(struct qcs *qcs, struct buffer *buf,
btype = htx_get_blk_type(blk);
fsize = bsize = htx_get_blksz(blk);
BUG_ON(btype == HTX_BLK_REQ_SL);
switch (btype) {
case HTX_BLK_REQ_SL:
res = qcc_get_stream_txbuf(qcs, &err, 0);
if (!res) {
BUG_ON(err); /* TODO */
goto end;
}
BUG_ON_HOT(sl); /* Only one start-line expected */
sl = htx_get_blk_ptr(htx, blk);
/* Only GET supported for HTTP/0.9. */
b_putist(res, ist("GET "));
b_putist(res, htx_sl_req_uri(sl));
b_putist(res, ist(" HTTP/0.9\r\n"));
htx_remove_blk(htx, blk);
total += fsize;
break;
case HTX_BLK_DATA:
res = qcc_get_stream_txbuf(qcs, &err, 0);
if (!res) {

View File

@ -1091,15 +1091,14 @@ void listener_accept(struct listener *l)
#endif
if (p && p->fe_sps_lim) {
int max = 0;
int it;
for (it = 0; it < global.nbtgroups; it++)
for (int it = 0; it < global.nbtgroups; it++)
max += freq_ctr_remain(&p->fe_counters.shared->tg[it]->sess_per_sec, p->fe_sps_lim, 0);
if (unlikely(!max)) {
unsigned int min_wait = 0;
for (it = 0; it < global.nbtgroups; it++) {
for (int it = 0; it < global.nbtgroups; it++) {
unsigned int cur_wait = next_event_delay(&p->fe_counters.shared->tg[it]->sess_per_sec, p->fe_sps_lim, 0);
if (!it || cur_wait < min_wait)
min_wait = cur_wait;

View File

@ -134,7 +134,6 @@ static struct qcs *qcs_new(struct qcc *qcc, uint64_t id, enum qcs_type type)
qcs->stream = NULL;
qcs->qcc = qcc;
qcs->sd = NULL;
qcs->flags = QC_SF_NONE;
qcs->st = QC_SS_IDLE;
qcs->ctx = NULL;
@ -3115,10 +3114,10 @@ static void qcc_shutdown(struct qcc *qcc)
TRACE_LEAVE(QMUX_EV_QCC_END, qcc->conn);
}
/* Loop through all qcs from <qcc> and wake their associated data layer if
* still active. Also report error on it if connection is already in error.
/* Loop through all qcs from <qcc>. Report error on stream endpoint if
* connection on error and wake them.
*/
static void qcc_wake_streams(struct qcc *qcc)
static int qcc_wake_some_streams(struct qcc *qcc)
{
struct qcs *qcs;
struct eb64_node *node;
@ -3135,10 +3134,11 @@ static void qcc_wake_streams(struct qcc *qcc)
if (qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL)) {
TRACE_POINT(QMUX_EV_QCC_WAKE, qcc->conn, qcs);
se_fl_set_error(qcs->sd);
qcs_alert(qcs);
}
qcs_alert(qcs);
}
return 0;
}
/* Conduct operations which should be made for <qcc> connection after
@ -3193,7 +3193,7 @@ static int qcc_io_process(struct qcc *qcc)
/* Report error if set on stream endpoint layer. */
if (qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL))
qcc_wake_streams(qcc);
qcc_wake_some_streams(qcc);
out:
if (qcc_is_dead(qcc))
@ -3406,7 +3406,6 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
{
struct qcc *qcc;
struct quic_transport_params *lparams, *rparams;
void *conn_ctx = conn->ctx;
TRACE_ENTER(QMUX_EV_QCC_NEW);
@ -3419,7 +3418,7 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
_qcc_init(qcc);
conn->ctx = qcc;
qcc->nb_hreq = qcc->nb_sc = 0;
qcc->flags = conn_is_back(conn) ? QC_CF_IS_BACK : 0;
qcc->flags = 0;
qcc->app_st = QCC_APP_ST_NULL;
qcc->glitches = 0;
qcc->err = quic_err_transport(QC_ERR_NO_ERROR);
@ -3532,48 +3531,27 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
if (qcc->app_ops == &h3_ops && !conn_is_back(conn))
proxy_inc_fe_cum_sess_ver_ctr(sess->listener, prx, 3);
if (!conn_is_back(conn)) {
/* Register conn for idle front closing. */
/* Register conn for idle front closing. This is done once everything is allocated. */
if (!conn_is_back(conn))
LIST_APPEND(&mux_stopping_data[tid].list, &conn->stopping_list);
/* init read cycle */
tasklet_wakeup(qcc->wait_event.tasklet);
/* init read cycle */
tasklet_wakeup(qcc->wait_event.tasklet);
/* MUX is initialized before QUIC handshake completion if early data
* received. Flag connection to delay stream processing if
* wait-for-handshake is active.
*/
if (conn->handle.qc->state < QUIC_HS_ST_COMPLETE) {
if (!(conn->flags & CO_FL_EARLY_SSL_HS)) {
TRACE_STATE("flag connection with early data", QMUX_EV_QCC_WAKE, conn);
conn->flags |= CO_FL_EARLY_SSL_HS;
/* subscribe for handshake completion */
conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV,
&qcc->wait_event);
qcc->flags |= QC_CF_WAIT_HS;
}
/* MUX is initialized before QUIC handshake completion if early data
* received. Flag connection to delay stream processing if
* wait-for-handshake is active.
*/
if (conn->handle.qc->state < QUIC_HS_ST_COMPLETE) {
if (!(conn->flags & CO_FL_EARLY_SSL_HS)) {
TRACE_STATE("flag connection with early data", QMUX_EV_QCC_WAKE, conn);
conn->flags |= CO_FL_EARLY_SSL_HS;
/* subscribe for handshake completion */
conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV,
&qcc->wait_event);
qcc->flags |= QC_CF_WAIT_HS;
}
}
else {
/* Initiate backend side transfer by creating the first
* bidirectional stream. MUX will then be woken up on QUIC
* handshake completion so that stream layer can start the
* transfer itself.
*/
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;
qcc->nb_sc++;
}
TRACE_LEAVE(QMUX_EV_QCC_NEW, conn);
return 0;
@ -3679,12 +3657,10 @@ static size_t qmux_strm_rcv_buf(struct stconn *sc, struct buffer *buf,
TRACE_STATE("report end-of-input", QMUX_EV_STRM_RECV, qcc->conn, qcs);
se_fl_set(qcs->sd, SE_FL_EOI);
if (!(qcc->flags & QC_CF_IS_BACK)) {
/* If request EOM is reported to the upper layer, it means the
* QCS now expects data from the opposite side.
*/
se_expect_data(qcs->sd);
}
/* If request EOM is reported to the upper layer, it means the
* QCS now expects data from the opposite side.
*/
se_expect_data(qcs->sd);
}
/* Set end-of-stream on read closed. */
@ -3966,11 +3942,7 @@ static int qmux_wake(struct connection *conn)
goto release;
}
/* Wake all streams, unless an error is set as qcc_io_process() has
* already woken them in this case.
*/
if (!(qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL)))
qcc_wake_streams(qcc);
qcc_wake_some_streams(qcc);
qcc_refresh_timeout(qcc);
@ -4177,6 +4149,6 @@ void qcc_show_quic(struct qcc *qcc)
}
static struct mux_proto_list mux_proto_quic =
{ .token = IST("quic"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &qmux_ops };
{ .token = IST("quic"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_FE, .mux = &qmux_ops };
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_quic);

View File

@ -286,11 +286,11 @@ int quic_connect_server(struct connection *conn, int flags)
struct proxy *be;
struct conn_src *src;
struct sockaddr_storage *addr;
struct quic_conn *qc = conn->handle.qc;
BUG_ON(qc->fd != -1);
BUG_ON(!conn->dst);
conn->flags |= CO_FL_WAIT_L4_CONN; /* connection in progress */
switch (obj_type(conn->target)) {
case OBJ_TYPE_PROXY:
be = __objt_proxy(conn->target);
@ -306,7 +306,7 @@ int quic_connect_server(struct connection *conn, int flags)
}
/* perform common checks on obtained socket FD, return appropriate Stream Error Flag in case of failure */
fd = sock_create_server_socket(conn, be, PROTO_TYPE_DGRAM, SOCK_DGRAM, &stream_err);
fd = conn->handle.fd = sock_create_server_socket(conn, be, &stream_err);
if (fd == -1)
return stream_err;
@ -413,25 +413,72 @@ int quic_connect_server(struct connection *conn, int flags)
}
if (global.tune.server_sndbuf)
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &global.tune.server_sndbuf, sizeof(global.tune.server_sndbuf));
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &global.tune.server_sndbuf, sizeof(global.tune.server_sndbuf));
if (global.tune.server_rcvbuf)
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &global.tune.server_rcvbuf, sizeof(global.tune.server_rcvbuf));
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &global.tune.server_rcvbuf, sizeof(global.tune.server_rcvbuf));
addr = (conn->flags & CO_FL_SOCKS4) ? &srv->socks4_addr : conn->dst;
if (connect(fd, (const struct sockaddr *)addr, get_addr_len(addr)) == -1) {
port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
fdinfo[fd].port_range = NULL;
close(fd);
conn->flags |= CO_FL_ERROR;
return SF_ERR_SRVCL;
if (errno == EINPROGRESS || errno == EALREADY) {
/* common case, let's wait for connect status */
conn->flags |= CO_FL_WAIT_L4_CONN;
}
else if (errno == EISCONN) {
/* should normally not happen but if so, indicates that it's OK */
conn->flags &= ~CO_FL_WAIT_L4_CONN;
}
else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EADDRINUSE || errno == EADDRNOTAVAIL) {
char *msg;
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EADDRNOTAVAIL) {
msg = "no free ports";
conn->err_code = CO_ER_FREE_PORTS;
}
else {
msg = "local address already in use";
conn->err_code = CO_ER_ADDR_INUSE;
}
qfprintf(stderr,"Connect() failed for backend %s: %s.\n", be->id, msg);
port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
fdinfo[fd].port_range = NULL;
close(fd);
send_log(be, LOG_ERR, "Connect() failed for backend %s: %s.\n", be->id, msg);
conn->flags |= CO_FL_ERROR;
return SF_ERR_RESOURCE;
} else if (errno == ETIMEDOUT) {
//qfprintf(stderr,"Connect(): ETIMEDOUT");
port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
fdinfo[fd].port_range = NULL;
close(fd);
conn->err_code = CO_ER_SOCK_ERR;
conn->flags |= CO_FL_ERROR;
return SF_ERR_SRVTO;
} else {
// (errno == ECONNREFUSED || errno == ENETUNREACH || errno == EACCES || errno == EPERM)
//qfprintf(stderr,"Connect(): %d", errno);
port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
fdinfo[fd].port_range = NULL;
close(fd);
conn->err_code = CO_ER_SOCK_ERR;
conn->flags |= CO_FL_ERROR;
return SF_ERR_SRVCL;
}
}
else {
/* connect() == 0, this is great! */
conn->flags &= ~CO_FL_WAIT_L4_CONN;
}
qc->fd = fd;
fd_insert(fd, qc, quic_conn_sock_fd_iocb, tgid, ti->ltid_bit);
fd_want_recv(fd);
conn_ctrl_init(conn); /* registers the FD */
HA_ATOMIC_OR(&fdtab[fd].state, FD_LINGER_RISK); /* close hard if needed */
if (conn->flags & CO_FL_WAIT_L4_CONN) {
fd_want_send(fd);
fd_cant_send(fd);
fd_cant_recv(fd);
}
conn_ctrl_init(conn);
return SF_ERR_NONE; /* connection is OK */
}

View File

@ -397,7 +397,7 @@ int tcp_connect_server(struct connection *conn, int flags)
/* perform common checks on obtained socket FD, return appropriate Stream Error Flag in case of failure */
fd = conn->handle.fd = sock_create_server_socket(conn, be, PROTO_TYPE_STREAM, SOCK_STREAM, &stream_err);
fd = conn->handle.fd = sock_create_server_socket(conn, be, &stream_err);
if (fd == -1)
return stream_err;

View File

@ -330,7 +330,7 @@ static int uxst_connect_server(struct connection *conn, int flags)
}
/* perform common checks on obtained socket FD, return appropriate Stream Error Flag in case of failure */
fd = conn->handle.fd = sock_create_server_socket(conn, be, PROTO_TYPE_STREAM, SOCK_STREAM, &stream_err);
fd = conn->handle.fd = sock_create_server_socket(conn, be, &stream_err);
if (fd == -1)
return stream_err;

View File

@ -145,7 +145,7 @@ void qmux_dump_qcc_info(struct buffer *msg, const struct qcc *qcc)
{
const struct quic_conn *qc = qcc->conn->handle.qc;
chunk_appendf(msg, " qcc=%p(%c)", qcc, (qcc->flags & QC_CF_IS_BACK) ? 'B' : 'F');
chunk_appendf(msg, " qcc=%p(F)", qcc);
if (qcc->conn->handle.qc)
chunk_appendf(msg, " qc=%p", qcc->conn->handle.qc);
chunk_appendf(msg, " .st=%s .sc=%llu .hreq=%llu .flg=0x%04x",

View File

@ -4,7 +4,7 @@
#include <haproxy/cli.h>
#include <haproxy/list.h>
#include <haproxy/mux_quic.h>
#include <haproxy/quic_conn.h>
#include <haproxy/quic_conn-t.h>
#include <haproxy/quic_tp.h>
#include <haproxy/quic_utils.h>
#include <haproxy/tools.h>
@ -181,11 +181,9 @@ static void dump_quic_oneline(struct show_quic_ctx *ctx, struct quic_conn *qc)
char bufaddr[INET6_ADDRSTRLEN], bufport[6];
int ret;
unsigned char cid_len;
struct listener *l = objt_listener(qc->target);
ret = chunk_appendf(&trash, "%p[%02u]/%-.12s ", qc, ctx->thr,
l ? l->bind_conf->frontend->id : __objt_server(qc->target)->id);
qc->li->bind_conf->frontend->id);
chunk_appendf(&trash, "%*s", 36 - ret, " "); /* align output */
/* State */

View File

@ -119,10 +119,6 @@ const struct quic_version quic_versions[] = {
},
};
const struct quic_version *quic_version_draft_29 = &quic_versions[0];
const struct quic_version *quic_version_1 = &quic_versions[1];
const struct quic_version *quic_version_2 = &quic_versions[2];
/* Function pointers, can be used to compute a hash from first generated CID and to derive new CIDs */
uint64_t (*quic_hash64_from_cid)(const unsigned char *cid, int size, const unsigned char *secret, size_t secretlen) = NULL;
void (*quic_newcid_from_hash64)(unsigned char *cid, int size, uint64_t hash, const unsigned char *secret, size_t secretlen) = NULL;
@ -749,7 +745,7 @@ static struct quic_conn_closed *qc_new_cc_conn(struct quic_conn *qc)
cc_qc->dcid = qc->dcid;
cc_qc->scid = qc->scid;
cc_qc->target = qc->target;
cc_qc->li = qc->li;
cc_qc->cids = qc->cids;
cc_qc->idle_timer_task = qc->idle_timer_task;
@ -775,7 +771,6 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
struct list send_list = LIST_HEAD_INIT(send_list);
struct quic_enc_level *qel;
int st;
int discard_hpktns = 0;
struct tasklet *tl = (struct tasklet *)t;
TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
@ -823,30 +818,16 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
goto out;
st = qc->state;
if (qc_is_listener(qc)) {
if (st >= QUIC_HS_ST_COMPLETE && !quic_tls_pktns_is_dcd(qc, qc->hpktns))
discard_hpktns = 1;
}
else {
if (st >= QUIC_HS_ST_CONFIRMED && !quic_tls_pktns_is_dcd(qc, qc->hpktns))
discard_hpktns = 1;
}
if (discard_hpktns) {
/* Discard the Handshake packet number space. */
TRACE_PROTO("discarding Handshake pktns", QUIC_EV_CONN_PHPKTS, qc);
quic_pktns_discard(qc->hel->pktns, qc);
qc_set_timer(qc);
qc_el_rx_pkts_del(qc->hel);
qc_release_pktns_frms(qc, qc->hel->pktns);
if (!qc_is_listener(qc)) {
/* I/O callback switch */
qc->wait_event.tasklet->process = quic_conn_app_io_cb;
if (st >= QUIC_HS_ST_COMPLETE) {
if (!(qc->flags & QUIC_FL_CONN_HPKTNS_DCD)) {
/* Discard the Handshake packet number space. */
TRACE_PROTO("discarding Handshake pktns", QUIC_EV_CONN_PHPKTS, qc);
quic_pktns_discard(qc->hel->pktns, qc);
qc_set_timer(qc);
qc_el_rx_pkts_del(qc->hel);
qc_release_pktns_frms(qc, qc->hel->pktns);
}
}
if (qc_is_listener(qc) && st >= QUIC_HS_ST_COMPLETE) {
/* Note: if no token for address validation was received
* for a 0RTT connection, some 0RTT packet could still be
* waiting for HP removal AFTER the successful handshake completion.
@ -904,26 +885,6 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
goto out;
}
/* RFC 9001. 4.9.1. Discarding Initial Keys
*
* The successful use of Handshake packets indicates that no more Initial
* packets need to be exchanged, as these keys can only be produced after
* receiving all CRYPTO frames from Initial packets. Thus, a client MUST
* discard Initial keys when it first sends a Handshake packet...
*/
if (!qc_is_listener(qc) && !quic_tls_pktns_is_dcd(qc, qc->ipktns) &&
qc->hpktns && qc->hpktns->tx.in_flight > 0) {
/* Discard the Initial packet number space. */
TRACE_PROTO("discarding Initial pktns", QUIC_EV_CONN_PRSHPKT, qc);
quic_pktns_discard(qc->ipktns, qc);
qc_set_timer(qc);
qc_el_rx_pkts_del(qc->iel);
qc_release_pktns_frms(qc, qc->ipktns);
}
qc_txb_release(qc);
out:
/* Release the Handshake encryption level and packet number space if
* the Handshake is confirmed and if there is no need to send
@ -1047,7 +1008,7 @@ struct task *qc_process_timer(struct task *task, void *ctx, unsigned int state)
/* Allocate a new QUIC connection with <version> as QUIC version. <ipv4>
* boolean is set to 1 for IPv4 connection, 0 for IPv6. <server> is set to 1
* for QUIC servers (or haproxy listeners), 0 for QUIC clients.
* for QUIC servers (or haproxy listeners).
* <dcid> is the destination connection ID, <scid> is the source connection ID.
* This latter <scid> CID as the same value on the wire as the one for <conn_id>
* which is the first CID of this connection but a different internal
@ -1060,9 +1021,6 @@ struct task *qc_process_timer(struct task *task, void *ctx, unsigned int state)
* into the Retry token sent to the client before instantiated this connection.
* Endpoints addresses are specified via <local_addr> and <peer_addr>.
* Returns the connection if succeeded, NULL if not.
* For QUIC clients, <dcid>, <scid>, <token_odcid>, <conn_id> must be null,
* and <token> value must be 0. This is the responsability of the caller to ensure
* this is the case.
*/
struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
struct quic_cid *dcid, struct quic_cid *scid,
@ -1070,13 +1028,11 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
struct quic_connection_id *conn_id,
struct sockaddr_storage *local_addr,
struct sockaddr_storage *peer_addr,
int token, void *target,
struct connection *conn)
int server, int token, void *owner)
{
struct quic_conn *qc = NULL;
struct listener *l = objt_listener(target);
struct server *srv = objt_server(target);
struct proxy *prx = l ? l->bind_conf->frontend : __objt_server(target)->proxy;
struct listener *l = server ? owner : NULL;
struct proxy *prx = l ? l->bind_conf->frontend : NULL;
struct quic_cc_algo *cc_algo = NULL;
unsigned int next_actconn = 0, next_sslconn = 0, next_handshake = 0;
@ -1095,7 +1051,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
goto err;
}
if (l) {
if (server) {
next_handshake = quic_increment_curr_handshake(l);
if (!next_handshake) {
TRACE_STATE("max handshake reached", QUIC_EV_CONN_INIT);
@ -1109,14 +1065,6 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
goto err;
}
qc->cids = pool_alloc(pool_head_quic_cids);
if (!qc->cids) {
TRACE_ERROR("Could not allocate a new CID tree", QUIC_EV_CONN_INIT, qc);
goto err;
}
qc->target = target;
*qc->cids = EB_ROOT;
/* Now that quic_conn instance is allocated, quic_conn_release() will
* ensure global accounting is decremented.
*/
@ -1134,6 +1082,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
qc->streams_by_id = EB_ROOT_UNIQUE;
/* Required to call free_quic_conn_cids() from quic_conn_release() */
qc->cids = NULL;
qc->tx.cc_buf_area = NULL;
qc_init_fd(qc);
@ -1147,7 +1096,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
qc->idle_timer_task = NULL;
qc->xprt_ctx = NULL;
qc->conn = conn;
qc->conn = NULL;
qc->qcc = NULL;
qc->app_ops = NULL;
qc->path = NULL;
@ -1168,12 +1117,16 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
/* Packet number spaces */
qc->ipktns = qc->hpktns = qc->apktns = NULL;
LIST_INIT(&qc->pktns_list);
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). */
if (l) {
if (server) {
cc_algo = l->bind_conf->quic_cc_algo;
qc->prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe,
&quic_stats_module);
qc->flags = QUIC_FL_CONN_LISTENER;
/* Mark this connection as having not received any token when 0-RTT is enabled. */
if (l->bind_conf->ssl_conf.early_data && !token)
@ -1184,52 +1137,29 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
/* Copy the packet SCID to reuse it as DCID for sending */
qc->dcid = *scid;
qc->tx.buf = BUF_NULL;
conn_id->qc = qc;
qc->li = l;
}
/* QUIC Client (outgoing connection to servers) */
else {
struct quic_connection_id *conn_cid = NULL;
qc->flags = QUIC_FL_CONN_PEER_VALIDATED_ADDR;
qc->state = QUIC_HS_ST_CLIENT_INITIAL;
memset(&qc->odcid, 0, sizeof qc->odcid);
qc->odcid.len = 0;
/* This is the original connection ID from the peer server
* point of view.
*/
if (RAND_bytes(qc->dcid.data, sizeof(qc->dcid.data)) != 1)
goto err;
qc->dcid.len = sizeof(qc->dcid.data);
conn_cid = new_quic_cid(qc->cids, qc, NULL, NULL);
if (!conn_cid)
goto err;
_quic_cid_insert(conn_cid);
dcid = &qc->dcid;
conn_id = conn_cid;
qc->tx.buf = BUF_NULL;
qc->next_cid_seq_num = 1;
conn->handle.qc = qc;
if (dcid->len)
memcpy(qc->dcid.data, dcid->data, dcid->len);
qc->dcid.len = dcid->len;
qc->li = NULL;
}
qc->mux_state = QC_MUX_NULL;
qc->err = quic_err_transport(QC_ERR_NO_ERROR);
/* Listener only: if connection is instantiated due to an INITIAL packet with an
/* If connection is instantiated due to an INITIAL packet with an
* already checked token, consider the peer address as validated.
*/
if (l) {
if (token_odcid->len) {
TRACE_STATE("validate peer address due to initial token",
QUIC_EV_CONN_INIT, qc);
qc->flags |= QUIC_FL_CONN_PEER_VALIDATED_ADDR;
}
else {
HA_ATOMIC_INC(&qc->prx_counters->half_open_conn);
}
if (token) {
TRACE_STATE("validate peer address due to initial token",
QUIC_EV_CONN_INIT, qc);
qc->flags |= QUIC_FL_CONN_PEER_VALIDATED_ADDR;
}
else {
HA_ATOMIC_INC(&qc->prx_counters->half_open_conn);
}
/* Now proceeds to allocation of qc members. */
@ -1239,8 +1169,16 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
goto err;
}
/* Listener only */
if (l && HA_ATOMIC_LOAD(&l->rx.quic_mode) == QUIC_SOCK_MODE_CONN &&
qc->cids = pool_alloc(pool_head_quic_cids);
if (!qc->cids) {
TRACE_ERROR("Could not allocate a new CID tree", QUIC_EV_CONN_INIT, qc);
goto err;
}
*qc->cids = EB_ROOT;
conn_id->qc = qc;
if (HA_ATOMIC_LOAD(&l->rx.quic_mode) == QUIC_SOCK_MODE_CONN &&
(quic_tune.options & QUIC_TUNE_SOCK_PER_CONN) &&
is_addr(local_addr)) {
TRACE_USER("Allocate a socket for QUIC connection", QUIC_EV_CONN_INIT, qc);
@ -1279,10 +1217,6 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
qc->rx.buf = b_make(qc->rx.buf.area, QUIC_CONN_RX_BUFSZ, 0, 0);
qc->rx.stream_max_uni = qc->rx.stream_max_bidi = 0;
qc->max_udp_payload = l ?
l->bind_conf->quic_params.max_udp_payload_size :
srv->quic_params.max_udp_payload_size;
qc->nb_pkt_for_cc = 1;
qc->nb_pkt_since_cc = 0;
@ -1294,24 +1228,17 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
qc->max_ack_delay = 0;
/* Only one path at this time (multipath not supported) */
qc->path = &qc->paths[0];
quic_cc_path_init(qc->path, ipv4, l ? l->bind_conf->max_cwnd : 0,
quic_cc_path_init(qc->path, ipv4, server ? l->bind_conf->max_cwnd : 0,
cc_algo ? cc_algo : default_quic_cc_algo, qc);
if (local_addr)
memcpy(&qc->local_addr, local_addr, sizeof(qc->local_addr));
else
memset(&qc->local_addr, 0, sizeof(qc->local_addr));
memcpy(&qc->local_addr, local_addr, sizeof(qc->local_addr));
memcpy(&qc->peer_addr, peer_addr, sizeof qc->peer_addr);
if (l) {
qc_lstnr_params_init(qc, &l->bind_conf->quic_params,
conn_id->stateless_reset_token,
qc->dcid.data, qc->dcid.len,
qc->scid.data, qc->scid.len, token_odcid);
}
else {
qc_srv_params_init(qc, &srv->quic_params, qc->scid.data, qc->scid.len);
}
if (server && !qc_lstnr_params_init(qc, &l->bind_conf->quic_params,
conn_id->stateless_reset_token,
dcid->data, dcid->len,
qc->scid.data, qc->scid.len, token_odcid))
goto err;
/* Initialize the idle timeout of the connection at the "max_idle_timeout"
* value from local transport parameters.
@ -1332,12 +1259,12 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
qc->wait_event.events = 0;
qc->subs = NULL;
if (qc_alloc_ssl_sock_ctx(qc, conn) ||
if (qc_alloc_ssl_sock_ctx(qc) ||
!quic_conn_init_timer(qc) ||
!quic_conn_init_idle_timer_task(qc, prx))
goto err;
if (!qc_new_isecs(qc, &qc->iel->tls_ctx, qc->original_version, dcid->data, dcid->len, !!l))
if (!qc_new_isecs(qc, &qc->iel->tls_ctx, qc->original_version, dcid->data, dcid->len, 1))
goto err;
/* Counters initialization */
@ -1351,15 +1278,6 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
return qc;
err:
if (!l && !conn_id) {
/* For QUIC clients, <conn_id> is locally used and initialized to <conn_cid>
* value as soon as this latter is attached to the CIDs tree. It must
* be freed only if it has not been attached to this tree. This is
* quic_conn_release() which free this CID when it is attached to the tree.
*/
pool_free(pool_head_quic_connection_id, conn_id);
}
quic_conn_release(qc);
/* Decrement global counters. Done only for errors happening before or
@ -1396,7 +1314,7 @@ int qc_handle_conn_migration(struct quic_conn *qc,
* used during the handshake, unless the endpoint has acted on a
* preferred_address transport parameter from the peer.
*/
if (__objt_listener(qc->target)->bind_conf->quic_params.disable_active_migration) {
if (qc->li->bind_conf->quic_params.disable_active_migration) {
TRACE_ERROR("Active migration was disabled, datagram dropped", QUIC_EV_CONN_LPKT, qc);
goto err;
}
@ -1526,8 +1444,8 @@ int quic_conn_release(struct quic_conn *qc)
*/
if (MT_LIST_INLIST(&qc->accept_list)) {
MT_LIST_DELETE(&qc->accept_list);
BUG_ON(__objt_listener(qc->target)->rx.quic_curr_accept == 0);
HA_ATOMIC_DEC(&__objt_listener(qc->target)->rx.quic_curr_accept);
BUG_ON(qc->li->rx.quic_curr_accept == 0);
HA_ATOMIC_DEC(&qc->li->rx.quic_curr_accept);
}
/* Substract last congestion window from global memory counter. */
@ -1597,8 +1515,8 @@ int quic_conn_release(struct quic_conn *qc)
/* Connection released before handshake completion. */
if (unlikely(qc->state < QUIC_HS_ST_COMPLETE)) {
if (qc_is_listener(qc)) {
BUG_ON(__objt_listener(qc->target)->rx.quic_curr_handshake == 0);
HA_ATOMIC_DEC(&__objt_listener(qc->target)->rx.quic_curr_handshake);
BUG_ON(qc->li->rx.quic_curr_handshake == 0);
HA_ATOMIC_DEC(&qc->li->rx.quic_curr_handshake);
}
}
@ -2006,8 +1924,8 @@ void qc_bind_tid_commit(struct quic_conn *qc, struct listener *new_li)
/* At this point no connection was accounted for yet on this
* listener so it's OK to just swap the pointer.
*/
if (new_li && new_li != __objt_listener(qc->target))
qc->target = &new_li->obj_type;
if (new_li && new_li != qc->li)
qc->li = new_li;
/* Rebind the connection FD. */
if (qc_test_fd(qc)) {

View File

@ -500,19 +500,11 @@ static int quic_parse_new_token_frame(struct quic_frame *frm, struct quic_conn *
{
struct qf_new_token *new_token_frm = &frm->new_token;
if (!quic_dec_int(&new_token_frm->len, pos, end) || end - *pos < new_token_frm->len)
if (!quic_dec_int(&new_token_frm->len, pos, end) || end - *pos < new_token_frm->len ||
sizeof(new_token_frm->data) < new_token_frm->len)
return 0;
/* TODO token length is unknown as it is dependent from the peer. Hence
* dynamic allocation should be implemented for token storage, albeit
* with constraint to ensure memory usage remains reasonable.
*/
#if 0
if (sizeof(new_token_frm->data) < new_token_frm->len)
return 0;
memcpy(new_token_frm->data, *pos, new_token_frm->len);
#endif
*pos += new_token_frm->len;
return 1;

View File

@ -58,7 +58,7 @@ static int qc_ssl_compat_add_tps_cb(SSL *ssl, unsigned int ext_type, unsigned in
int quic_tls_compat_init(struct bind_conf *bind_conf, SSL_CTX *ctx)
{
/* Ignore non-QUIC connections */
if (bind_conf && bind_conf->xprt != xprt_get(XPRT_QUIC))
if (bind_conf->xprt != xprt_get(XPRT_QUIC))
return 1;
/* This callback is already registered if the TLS keylog is activated for
@ -150,22 +150,22 @@ void quic_tls_compat_keylog_callback(const SSL *ssl, const char *line)
if (sizeof(QUIC_OPENSSL_COMPAT_CLIENT_HANDSHAKE) - 1 == n &&
!strncmp(start, QUIC_OPENSSL_COMPAT_CLIENT_HANDSHAKE, n)) {
level = ssl_encryption_handshake;
write = qc_is_listener(qc) ? 0 : 1;
write = 0;
}
else if (sizeof(QUIC_OPENSSL_COMPAT_SERVER_HANDSHAKE) - 1 == n &&
!strncmp(start, QUIC_OPENSSL_COMPAT_SERVER_HANDSHAKE, n)) {
level = ssl_encryption_handshake;
write = qc_is_listener(qc) ? 1 : 0;
write = 1;
}
else if (sizeof(QUIC_OPENSSL_COMPAT_CLIENT_APPLICATION) - 1 == n &&
!strncmp(start, QUIC_OPENSSL_COMPAT_CLIENT_APPLICATION, n)) {
level = ssl_encryption_application;
write = qc_is_listener(qc) ? 0 : 1;
write = 0;
}
else if (sizeof(QUIC_OPENSSL_COMPAT_SERVER_APPLICATION) - 1 == n &&
!strncmp(start, QUIC_OPENSSL_COMPAT_SERVER_APPLICATION, n)) {
level = ssl_encryption_application;
write = qc_is_listener(qc) ? 1 : 0;
write = 1;
}
else
goto leave;

View File

@ -949,11 +949,7 @@ static int qc_parse_pkt_frms(struct quic_conn *qc, struct quic_rx_packet *pkt,
goto err;
}
else {
/* TODO NEW_TOKEN not implemented on client side.
* Note that for now token is not copied into <data> field
* of qf_new_token frame. See quic_parse_new_token_frame()
* for further explanations.
*/
/* TODO */
}
break;
case QUIC_FT_STREAM_8 ... QUIC_FT_STREAM_F:
@ -1823,8 +1819,8 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt,
goto err;
qc = qc_new_conn(pkt->version, ipv4, &pkt->dcid, &pkt->scid, &token_odcid,
conn_id, &dgram->daddr, &pkt->saddr,
!!pkt->token_len, l, NULL);
conn_id, &dgram->daddr, &pkt->saddr, 1,
!!pkt->token_len, l);
if (qc == NULL) {
pool_free(pool_head_quic_connection_id, conn_id);
goto err;
@ -1881,25 +1877,25 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt,
/* Parse a QUIC packet starting at <pos>. Data won't be read after <end> even
* if the packet is incomplete. This function will populate fields of <pkt>
* instance, most notably its length. <dgram> is the UDP datagram which
* contains the parsed packet. <o> is the address object type address of the
* object which receives this received packet. <qc> is the QUIC connection,
* only valid for QUIC clients.
* contains the parsed packet. <l> is the listener instance on which it was
* received.
*
* Returns 0 on success else non-zero. Packet length is guaranteed to be set to
* the real packet value or to cover all data between <pos> and <end> : this is
* useful to reject a whole datagram.
*/
static int quic_rx_pkt_parse(struct quic_conn *qc, struct quic_rx_packet *pkt,
static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
unsigned char *pos, const unsigned char *end,
struct quic_dgram *dgram, enum obj_type *o)
struct quic_dgram *dgram, struct listener *l)
{
const unsigned char *beg = pos;
struct proxy *prx;
struct quic_counters *prx_counters;
struct listener *l = objt_listener(o);
TRACE_ENTER(QUIC_EV_CONN_LPKT);
prx_counters = qc_counters(o, &quic_stats_module);
prx = l->bind_conf->frontend;
prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module);
if (end <= pos) {
TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT);
@ -1955,10 +1951,8 @@ static int quic_rx_pkt_parse(struct quic_conn *qc, struct quic_rx_packet *pkt,
goto drop;
}
/* RFC9000 6. Version Negotiation. A Version Negotiation packet is
* sent only by servers.
*/
if (l && !pkt->version) {
/* RFC9000 6. Version Negotiation */
if (!pkt->version) {
/* unsupported version, send Negotiation packet */
if (send_version_negotiation(l->rx.fd, &dgram->saddr, pkt)) {
TRACE_ERROR("VN packet not sent", QUIC_EV_CONN_LPKT);
@ -1982,13 +1976,6 @@ static int quic_rx_pkt_parse(struct quic_conn *qc, struct quic_rx_packet *pkt,
goto drop;
}
if (!l && pkt->token_len) {
/* A server must sent Initial packets with a null token length. */
TRACE_PROTO("Packet dropped",
QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version);
goto drop;
}
pkt->token = pos;
pkt->token_len = token_len;
pos += pkt->token_len;
@ -2014,49 +2001,26 @@ static int quic_rx_pkt_parse(struct quic_conn *qc, struct quic_rx_packet *pkt,
pkt->pn_offset = pos - beg;
pkt->len = pkt->pn_offset + len;
/* RFC 9000. Initial Datagram Size
*
* A server MUST discard an Initial packet that is carried in a UDP datagram
* with a payload that is smaller than the smallest allowed maximum datagram
* size of 1200 bytes.
*/
if (pkt->type == QUIC_PACKET_TYPE_INITIAL &&
dgram->len < QUIC_INITIAL_PACKET_MINLEN) {
TRACE_PROTO("RX too short datagram with an Initial packet", QUIC_EV_CONN_LPKT);
HA_ATOMIC_INC(&prx_counters->too_short_initial_dgram);
goto drop;
}
/* Interrupt parsing after packet length retrieval : this
* ensures that only the packet is dropped but not the whole
* datagram.
*/
if (pkt->type == QUIC_PACKET_TYPE_INITIAL) {
if (l) {
/* RFC 9000. Initial Datagram Size
*
* A server MUST discard an Initial packet that is carried in a UDP datagram
* with a payload that is smaller than the smallest allowed maximum datagram
* size of 1200 bytes.
*/
if (dgram->len < QUIC_INITIAL_PACKET_MINLEN) {
TRACE_PROTO("RX too short datagram with an Initial packet", QUIC_EV_CONN_LPKT);
HA_ATOMIC_INC(&prx_counters->too_short_initial_dgram);
goto drop;
}
}
else {
/* TODO: This is not clear if a too short RX datagram which carries ack-eliciting
* packets must be dropped by a client. If this is the case, this is not from
* here, but after having parsed the datagram frames.
*
* RFC 9000. Initial Datagram Size
*
* Similarly, a server MUST expand the payload of all UDP datagrams carrying
* ack-eliciting Initial packets to at least the smallest allowed maximum
* datagram size of 1200 bytes.
*/
if (!(qc->flags & QUIC_FL_CONN_SCID_RECEIVED)) {
qc->flags |= QUIC_FL_CONN_SCID_RECEIVED;
memcpy(qc->dcid.data, pkt->scid.data, pkt->scid.len);
qc->dcid.len = pkt->scid.len;
}
}
}
else if (pkt->type == QUIC_PACKET_TYPE_0RTT) {
/* O-RTT packet are not sent by servers. */
if (!l || !l->bind_conf->ssl_conf.early_data) {
TRACE_PROTO("RX 0-RTT packet not supported", QUIC_EV_CONN_LPKT);
goto drop;
}
if (pkt->type == QUIC_PACKET_TYPE_0RTT && !l->bind_conf->ssl_conf.early_data) {
TRACE_PROTO("RX 0-RTT packet not supported", QUIC_EV_CONN_LPKT);
goto drop;
}
}
else {
@ -2278,8 +2242,7 @@ static void qc_rx_pkt_handle(struct quic_conn *qc, struct quic_rx_packet *pkt,
* this function.
*
* If datagram has been received on a quic-conn owned FD, <from_qc> must be set
* to the connection instance. <o> is the object type address of the object
* (listener or server) receiving the datagram. The caller is
* to the connection instance. <li> is the attached listener. The caller is
* responsible to ensure that the first packet is destined to this connection
* by comparing CIDs.
*
@ -2291,7 +2254,7 @@ static void qc_rx_pkt_handle(struct quic_conn *qc, struct quic_rx_packet *pkt,
* the datagram may not have been parsed.
*/
int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
enum obj_type *o)
struct listener *li)
{
struct quic_rx_packet *pkt;
struct quic_conn *qc = NULL;
@ -2329,7 +2292,7 @@ int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
pkt->flags |= QUIC_FL_RX_PACKET_DGRAM_FIRST;
quic_rx_packet_refinc(pkt);
if (quic_rx_pkt_parse(from_qc, pkt, pos, end, dgram, o))
if (quic_rx_pkt_parse(pkt, pos, end, dgram, li))
goto next;
/* Search quic-conn instance for first packet of the datagram.
@ -2338,7 +2301,6 @@ int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
*/
if (!qc) {
int new_tid = -1;
struct listener *li = objt_listener(o);
qc = from_qc ? from_qc : quic_rx_pkt_retrieve_conn(pkt, dgram, li, &new_tid);
/* qc is NULL if receiving a non Initial packet for an

View File

@ -87,13 +87,13 @@ int quic_sock_get_dst(struct connection *conn, struct sockaddr *addr, socklen_t
memcpy(addr, &qc->peer_addr, len);
} else {
struct sockaddr_storage *from;
struct listener *l = objt_listener(qc->target);
/* Return listener address if IP_PKTINFO or friends are not
* supported by the socket.
*/
BUG_ON(!l);
from = is_addr(&qc->local_addr) ? &qc->local_addr : &l->rx.addr;
BUG_ON(!qc->li);
from = is_addr(&qc->local_addr) ? &qc->local_addr :
&qc->li->rx.addr;
if (len > sizeof(*from))
len = sizeof(*from);
memcpy(addr, from, len);
@ -815,30 +815,36 @@ int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz,
int qc_rcv_buf(struct quic_conn *qc)
{
struct sockaddr_storage saddr = {0}, daddr = {0};
struct quic_transport_params *params;
struct quic_dgram *new_dgram = NULL;
struct buffer buf = BUF_NULL;
size_t max_sz;
unsigned char *dgram_buf;
struct listener *l;
ssize_t ret = 0;
struct listener *l = objt_listener(qc->target);
/* Do not call this if quic-conn FD is uninitialized. */
BUG_ON(qc->fd < 0);
TRACE_ENTER(QUIC_EV_CONN_RCV, qc);
l = qc->li;
params = &l->bind_conf->quic_params;
max_sz = params->max_udp_payload_size;
do {
if (!b_alloc(&buf, DB_MUX_RX))
break; /* TODO subscribe for memory again available. */
b_reset(&buf);
BUG_ON(b_contig_space(&buf) < qc->max_udp_payload);
BUG_ON(b_contig_space(&buf) < max_sz);
/* Allocate datagram on first loop or after requeuing. */
if (!new_dgram && !(new_dgram = pool_alloc(pool_head_quic_dgram)))
break; /* TODO subscribe for memory again available. */
dgram_buf = (unsigned char *)b_tail(&buf);
ret = quic_recv(qc->fd, dgram_buf, qc->max_udp_payload,
ret = quic_recv(qc->fd, dgram_buf, max_sz,
(struct sockaddr *)&saddr, sizeof(saddr),
(struct sockaddr *)&daddr, sizeof(daddr),
get_net_port(&qc->local_addr));
@ -870,7 +876,7 @@ int qc_rcv_buf(struct quic_conn *qc)
continue;
}
if (l && !qc_check_dcid(qc, new_dgram->dcid, new_dgram->dcid_len)) {
if (!qc_check_dcid(qc, new_dgram->dcid, new_dgram->dcid_len)) {
/* Datagram received by error on the connection FD, dispatch it
* to its associated quic-conn.
*
@ -928,7 +934,7 @@ int qc_rcv_buf(struct quic_conn *qc)
continue;
}
quic_dgram_parse(new_dgram, qc, qc->target);
quic_dgram_parse(new_dgram, qc, qc->li);
/* A datagram must always be consumed after quic_parse_dgram(). */
BUG_ON(new_dgram->buf);
} while (ret > 0);
@ -950,13 +956,11 @@ int qc_rcv_buf(struct quic_conn *qc)
*
* Return the socket FD or a negative error code. On error, socket is marked as
* uninitialized.
* Note: This function must not be run for backends connection.
*/
void qc_alloc_fd(struct quic_conn *qc, const struct sockaddr_storage *src,
const struct sockaddr_storage *dst)
{
struct listener *l = __objt_listener(qc->target);
struct bind_conf *bc = l->bind_conf;
struct bind_conf *bc = qc->li->bind_conf;
struct proxy *p = bc->frontend;
int fd = -1;
int ret;
@ -1009,7 +1013,7 @@ void qc_alloc_fd(struct quic_conn *qc, const struct sockaddr_storage *src,
}
/* Fallback to listener socket for this receiver instance. */
HA_ATOMIC_STORE(&l->rx.quic_mode, QUIC_SOCK_MODE_LSTNR);
HA_ATOMIC_STORE(&qc->li->rx.quic_mode, QUIC_SOCK_MODE_LSTNR);
}
goto err;
}
@ -1063,14 +1067,13 @@ struct quic_accept_queue *quic_accept_queues;
void quic_accept_push_qc(struct quic_conn *qc)
{
struct quic_accept_queue *queue = &quic_accept_queues[tid];
struct listener *l = __objt_listener(qc->target);
struct li_per_thread *lthr = &l->per_thr[ti->ltid];
struct li_per_thread *lthr = &qc->li->per_thr[ti->ltid];
/* A connection must only be accepted once per instance. */
BUG_ON(qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED);
BUG_ON(MT_LIST_INLIST(&qc->accept_list));
HA_ATOMIC_INC(&l->rx.quic_curr_accept);
HA_ATOMIC_INC(&qc->li->rx.quic_curr_accept);
qc->flags |= QUIC_FL_CONN_ACCEPT_REGISTERED;
/* 1. insert the listener in the accept queue

View File

@ -17,7 +17,7 @@ DECLARE_POOL(pool_head_quic_ssl_sock_ctx, "quic_ssl_sock_ctx", sizeof(struct ssl
* be set to 1 for a QUIC server, 0 for a client.
* Return 1 if succeeded, 0 if not.
*/
static int qc_ssl_set_quic_transport_params(SSL *ssl, struct quic_conn *qc,
static int qc_ssl_set_quic_transport_params(struct quic_conn *qc,
const struct quic_version *ver, int server)
{
int ret = 0;
@ -40,7 +40,7 @@ static int qc_ssl_set_quic_transport_params(SSL *ssl, struct quic_conn *qc,
goto leave;
}
if (!SSL_set_quic_transport_params(ssl, in, *enclen)) {
if (!SSL_set_quic_transport_params(qc->xprt_ctx->ssl, in, *enclen)) {
TRACE_ERROR("SSL_set_quic_transport_params() failed", QUIC_EV_CONN_RWSEC);
goto leave;
}
@ -274,30 +274,16 @@ write:
/* Set the transport parameters in the TLS stack. */
if (level == ssl_encryption_handshake && qc_is_listener(qc) &&
!qc_ssl_set_quic_transport_params(qc->xprt_ctx->ssl, qc, ver, 1))
!qc_ssl_set_quic_transport_params(qc, ver, 1))
goto leave;
keyupdate_init:
/* Store the secret provided by the TLS stack, required for keyupdate. */
if (level == ssl_encryption_application) {
struct quic_tls_kp *prv_rx = &qc->ku.prv_rx;
struct quic_tls_kp *nxt_rx = &qc->ku.nxt_rx;
struct quic_tls_kp *nxt_tx = &qc->ku.nxt_tx;
#if !defined(USE_QUIC_OPENSSL_COMPAT) && !defined(HAVE_OPENSSL_QUIC)
if (!qc_is_listener(qc)) {
const unsigned char *tp;
size_t tplen;
SSL_get_peer_quic_transport_params(ssl, &tp, &tplen);
if (!tplen || !quic_transport_params_store(qc, 1,tp, tp + tplen)) {
TRACE_ERROR("Could not parse remote transport paratemers",
QUIC_EV_CONN_RWSEC, qc);
goto leave;
}
}
#endif
/* Store the secret provided by the TLS stack, required for keyupdate. */
if (rx) {
if (!(rx->secret = pool_alloc(pool_head_quic_tls_secret))) {
TRACE_ERROR("Could not allocate RX Application secrete keys", QUIC_EV_CONN_RWSEC, qc);
@ -584,7 +570,7 @@ static int ha_quic_ossl_got_transport_params(SSL *ssl, const unsigned char *para
}
else {
if (!quic_transport_params_store(qc, 0, params, params + params_len) ||
!qc_ssl_set_quic_transport_params(ssl, qc, ver, 1))
!qc_ssl_set_quic_transport_params(qc, ver, 1))
goto err;
}
@ -768,38 +754,6 @@ int ssl_quic_initial_ctx(struct bind_conf *bind_conf)
return cfgerr;
}
/* Allocate a TLS context for a QUIC server.
* Return this context if succeeded, NULL if failed.
*/
SSL_CTX *ssl_quic_srv_new_ssl_ctx(void)
{
SSL_CTX *ctx = NULL;
/* XXX TODO: check this: XXX */
long options =
(SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
SSL_OP_SINGLE_ECDH_USE |
SSL_OP_CIPHER_SERVER_PREFERENCE;
ctx = SSL_CTX_new(TLS_client_method());
if (!ctx)
goto err;
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:
return ctx;
err:
SSL_CTX_free(ctx);
ctx = NULL;
goto leave;
}
/* This function gives the detail of the SSL error. It is used only
* if the debug mode and the verbose mode are activated. It dump all
* the SSL error until the stack was empty.
@ -931,54 +885,28 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
* provided by the stack. This happens after having received the peer
* handshake level CRYPTO data which are validated by the TLS stack.
*/
if (qc_is_listener(qc)) {
if (__objt_listener(qc->target)->bind_conf->ssl_conf.early_data &&
(!qc->ael || !qc->ael->tls_ctx.rx.secret)) {
TRACE_PROTO("SSL handshake in progress",
QUIC_EV_CONN_IO_CB, qc, &state, &ssl_err);
goto out;
}
else {
TRACE_PROTO("SSL handshake OK", QUIC_EV_CONN_IO_CB, qc, &state);
}
if (qc->li->bind_conf->ssl_conf.early_data &&
(!qc->ael || !qc->ael->tls_ctx.rx.secret)) {
TRACE_PROTO("SSL handshake in progress",
QUIC_EV_CONN_IO_CB, qc, &state, &ssl_err);
goto out;
}
else {
TRACE_PROTO("SSL handshake OK", QUIC_EV_CONN_IO_CB, qc, &state);
}
#endif
/* Check the alpn could be negotiated */
if (qc_is_listener(qc)) {
if (!qc->app_ops) {
TRACE_ERROR("No negotiated ALPN", QUIC_EV_CONN_IO_CB, qc, &state);
quic_set_tls_alert(qc, SSL_AD_NO_APPLICATION_PROTOCOL);
goto leave;
}
}
else {
const unsigned char *alpn;
size_t alpn_len;
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;
}
if (conn_create_mux(ctx->conn, NULL) < 0) {
TRACE_ERROR("mux creation failed", QUIC_EV_CONN_IO_CB, qc, &state);
goto leave;
}
/* Wake up MUX after its creation. Operation similar to TLS+ALPN on TCP stack. */
ctx->conn->mux->wake(ctx->conn);
qc->mux_state = QC_MUX_READY;
if (!qc->app_ops) {
TRACE_ERROR("No negotiated ALPN", QUIC_EV_CONN_IO_CB, qc, &state);
quic_set_tls_alert(qc, SSL_AD_NO_APPLICATION_PROTOCOL);
goto leave;
}
qc->flags |= QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS;
/* I/O callback switch */
qc->wait_event.tasklet->process = quic_conn_app_io_cb;
if (qc_is_listener(ctx->qc)) {
struct listener *l = __objt_listener(qc->target);
/* I/O callback switch */
qc->wait_event.tasklet->process = quic_conn_app_io_cb;
qc->flags |= QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS;
qc->state = QUIC_HS_ST_CONFIRMED;
if (!(qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED)) {
@ -992,8 +920,8 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
tasklet_wakeup(qc->wait_event.tasklet);
}
BUG_ON(l->rx.quic_curr_handshake == 0);
HA_ATOMIC_DEC(&l->rx.quic_curr_handshake);
BUG_ON(qc->li->rx.quic_curr_handshake == 0);
HA_ATOMIC_DEC(&qc->li->rx.quic_curr_handshake);
}
else {
qc->state = QUIC_HS_ST_COMPLETE;
@ -1113,8 +1041,7 @@ int quic_ssl_set_tls_cbs(SSL *ssl)
* Return 0 if succeeded, -1 if not. If failed, sets the ->err_code member of <qc->conn> to
* CO_ER_SSL_NO_MEM.
*/
static int qc_ssl_sess_init(struct quic_conn *qc, SSL_CTX *ssl_ctx, SSL **ssl,
struct connection *conn, int server)
static int qc_ssl_sess_init(struct quic_conn *qc, SSL_CTX *ssl_ctx, SSL **ssl)
{
int retry, ret = -1;
@ -1132,7 +1059,6 @@ static int qc_ssl_sess_init(struct quic_conn *qc, SSL_CTX *ssl_ctx, SSL **ssl,
}
if (!SSL_set_ex_data(*ssl, ssl_qc_app_data_index, qc) ||
(!server && !SSL_set_ex_data(*ssl, ssl_app_data_index, conn)) ||
!quic_ssl_set_tls_cbs(*ssl)) {
SSL_free(*ssl);
*ssl = NULL;
@ -1195,9 +1121,10 @@ static int qc_set_quic_early_data_enabled(struct quic_conn *qc, SSL *ssl)
*
* Returns 0 on success else non-zero.
*/
int qc_alloc_ssl_sock_ctx(struct quic_conn *qc, struct connection *conn)
int qc_alloc_ssl_sock_ctx(struct quic_conn *qc)
{
int ret = 0;
struct bind_conf *bc = qc->li->bind_conf;
struct ssl_sock_ctx *ctx = NULL;
TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
@ -1208,7 +1135,7 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc, struct connection *conn)
goto err;
}
ctx->conn = conn;
ctx->conn = NULL;
ctx->bio = NULL;
ctx->xprt = NULL;
ctx->xprt_ctx = NULL;
@ -1221,9 +1148,7 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc, struct connection *conn)
ctx->qc = qc;
if (qc_is_listener(qc)) {
struct bind_conf *bc = __objt_listener(qc->target)->bind_conf;
if (qc_ssl_sess_init(qc, bc->initial_ctx, &ctx->ssl, NULL, 1) == -1)
if (qc_ssl_sess_init(qc, bc->initial_ctx, &ctx->ssl) == -1)
goto err;
#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L) && defined(HAVE_SSL_0RTT_QUIC)
/* Enabling 0-RTT */
@ -1233,36 +1158,6 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc, struct connection *conn)
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);

View File

@ -213,18 +213,9 @@ static int quic_transport_param_dec_version_info(struct tp_version_information *
return 0;
}
if (server) {
int i;
for (i = 0; i < quic_versions_nb; i++) {
if (tp->chosen == quic_versions[i].num) {
tp->negotiated_version = &quic_versions[i];
goto out;
}
}
if (server)
/* TODO: not supported */
return 0;
}
for (ver = others; ver < end; ver += 4) {
if (!tp->negotiated_version) {
@ -522,21 +513,8 @@ static int quic_transport_param_enc_version_info(unsigned char **buf,
memcpy(*buf, &ver, sizeof ver);
*buf += sizeof ver;
/* For servers: all supported version, chosen included */
if (server) {
for (i = 0; i < quic_versions_nb; i++) {
ver = htonl(quic_versions[i].num);
memcpy(*buf, &ver, sizeof ver);
*buf += sizeof ver;
}
}
else {
ver = htonl(quic_version_1->num);
memcpy(*buf, &ver, sizeof ver);
*buf += sizeof ver;
ver = htonl(quic_version_2->num);
memcpy(*buf, &ver, sizeof ver);
*buf += sizeof ver;
ver = htonl(quic_version_draft_29->num);
for (i = 0; i < quic_versions_nb; i++) {
ver = htonl(quic_versions[i].num);
memcpy(*buf, &ver, sizeof ver);
*buf += sizeof ver;
}
@ -828,21 +806,3 @@ int qc_lstnr_params_init(struct quic_conn *qc,
return 1;
}
/* QUIC client (or haproxy server) only function.
* Initialize the local transport parameters <rx_params> from <srv_params>
* coming from configuration and source connection ID).
* Never fails.
*/
void qc_srv_params_init(struct quic_conn *qc,
const struct quic_transport_params *srv_params,
const unsigned char *scid, size_t scidlen)
{
struct quic_transport_params *rx_params = &qc->rx.params;
/* Copy the transport parameters. */
*rx_params = *srv_params;
/* Copy the initial source connection ID. */
memcpy(rx_params->initial_source_connection_id.data, scid, scidlen);
rx_params->initial_source_connection_id.len = scidlen;
TRACE_PROTO("\nRX(local) transp. params.", QUIC_EV_TRANSP_PARAMS, qc, rx_params);
}

View File

@ -117,13 +117,6 @@ static void quic_trace(enum trace_level level, uint64_t mask, const struct trace
chunk_appendf(&trace_buf, " : qc@%p idle_timer_task@%p flags=0x%x",
qc, qc->idle_timer_task, qc->flags);
if (mask & QUIC_EV_CONN_NEW) {
const int *ssl_err = a2;
if (ssl_err)
chunk_appendf(&trace_buf, " ssl_err=%d", *ssl_err);
}
if (mask & QUIC_EV_CONN_INIT) {
chunk_appendf(&trace_buf, "\n odcid");
quic_cid_dump(&trace_buf, &qc->odcid);

View File

@ -288,10 +288,8 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx)
int ret = 0;
struct quic_conn *qc;
char skip_sendto = 0;
struct listener *l;
qc = ctx->qc;
l = objt_listener(qc->target);
TRACE_ENTER(QUIC_EV_CONN_SPPKTS, qc);
while (b_contig_data(buf, 0)) {
unsigned char *pos;
@ -307,11 +305,7 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx)
/* If datagram bigger than MTU, several ones were encoded for GSO usage. */
if (dglen > qc->path->mtu) {
/* 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))) {
if (likely(!(HA_ATOMIC_LOAD(&qc->li->flags) & LI_F_UDP_GSO_NOTSUPP))) {
TRACE_PROTO("send multiple datagrams with GSO", QUIC_EV_CONN_SPPKTS, qc);
gso = qc->path->mtu;
}
@ -333,15 +327,11 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx)
int ret = qc_snd_buf(qc, &tmpbuf, tmpbuf.data, 0, gso);
if (ret < 0) {
if (gso && ret == -EIO) {
/* TODO: note that at this time for connection to backends this
* part is not run because no more than an MTU has been
* prepared for such connections (l is not NULL).
*/
/* Disable permanently UDP GSO for this listener.
* Retry standard emission.
*/
TRACE_ERROR("mark listener UDP GSO as unsupported", QUIC_EV_CONN_SPPKTS, qc, first_pkt);
HA_ATOMIC_OR(&l->flags, LI_F_UDP_GSO_NOTSUPP);
HA_ATOMIC_OR(&qc->li->flags, LI_F_UDP_GSO_NOTSUPP);
continue;
}
@ -586,7 +576,6 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
int dgram_cnt = 0;
/* Restrict GSO emission to comply with sendmsg limitation. See QUIC_MAX_GSO_DGRAMS for more details. */
uchar gso_dgram_cnt = 0;
struct listener *l = objt_listener(qc->target);
TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
/* Currently qc_prep_pkts() does not handle buffer wrapping so the
@ -776,13 +765,11 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
prv_pkt = cur_pkt;
}
else if (!(quic_tune.options & QUIC_TUNE_NO_UDP_GSO) &&
!(HA_ATOMIC_LOAD(&qc->li->flags) & LI_F_UDP_GSO_NOTSUPP) &&
dglen == qc->path->mtu &&
(char *)end < b_wrap(buf) &&
++gso_dgram_cnt < QUIC_MAX_GSO_DGRAMS &&
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.
*/
++gso_dgram_cnt < QUIC_MAX_GSO_DGRAMS) {
/* A datagram covering the full MTU has been
* built, use GSO to built next entry. Do not
* reserve extra space for datagram header.

View File

@ -37,7 +37,6 @@
#include <haproxy/protocol.h>
#include <haproxy/proxy.h>
#include <haproxy/queue.h>
#include <haproxy/quic_tp.h>
#include <haproxy/resolvers.h>
#include <haproxy/sample.h>
#include <haproxy/sc_strm.h>
@ -2719,7 +2718,7 @@ static void srv_ssl_settings_cpy(struct server *srv, const struct server *src)
srv->ssl_ctx.client_crt = strdup(src->ssl_ctx.client_crt);
srv->ssl_ctx.verify = src->ssl_ctx.verify;
srv->ssl_ctx.renegotiate = src->ssl_ctx.renegotiate;
if (src->ssl_ctx.verify_host != NULL)
srv->ssl_ctx.verify_host = strdup(src->ssl_ctx.verify_host);
@ -3590,7 +3589,6 @@ static int _srv_parse_init(struct server **srv, char **args, int *cur_arg,
(parse_flags & SRV_PARSE_INITIAL_RESOLVE ? PA_O_RESOLVE : 0) | PA_O_PORT_OK |
(parse_flags & SRV_PARSE_IN_PEER_SECTION ? PA_O_PORT_MAND : PA_O_PORT_OFS) |
PA_O_STREAM | PA_O_DGRAM | PA_O_XPRT);
if (!sk) {
ha_alert("%s\n", errmsg);
err_code |= ERR_ALERT | ERR_FATAL;
@ -3598,21 +3596,6 @@ static int _srv_parse_init(struct server **srv, char **args, int *cur_arg,
goto out;
}
#ifdef USE_QUIC
if (srv_is_quic(newsrv)) {
if (!experimental_directives_allowed) {
ha_alert("QUIC is experimental for server '%s',"
" must be allowed via a global 'expose-experimental-directives'\n",
newsrv->id);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
newsrv->xprt = xprt_get(XPRT_QUIC);
quic_transport_params_init(&newsrv->quic_params, 0);
}
#endif
if (!port1 || !port2) {
if (sk->ss_family != AF_CUST_RHTTP_SRV) {
/* no port specified, +offset, -offset */
@ -3836,15 +3819,6 @@ static int _srv_parse_finalize(char **args, int cur_arg,
}
}
#ifdef USE_QUIC
if (srv_is_quic(srv)) {
if (!srv->use_ssl) {
ha_alert("QUIC protocol detected without explicit SSL requirement. Use 'ssl' to fix this.\n");
return ERR_ALERT | ERR_FATAL;
}
}
#endif
srv_lb_commit_status(srv);
return 0;

View File

@ -265,8 +265,7 @@ static int sock_handle_system_err(struct connection *conn, struct proxy *be)
* upper level is set as SF_ERR_NONE; -1 on failure, stream_err is set to
* appropriate value.
*/
int sock_create_server_socket(struct connection *conn, struct proxy *be,
enum proto_type proto_type, int sock_type, int *stream_err)
int sock_create_server_socket(struct connection *conn, struct proxy *be, int *stream_err)
{
const struct netns_entry *ns = NULL;
const struct protocol *proto;
@ -280,9 +279,9 @@ int sock_create_server_socket(struct connection *conn, struct proxy *be,
ns = __objt_server(conn->target)->netns;
}
#endif
proto = protocol_lookup(conn->dst->ss_family, proto_type , conn->ctrl->sock_prot == IPPROTO_MPTCP);
proto = protocol_lookup(conn->dst->ss_family, PROTO_TYPE_STREAM, conn->ctrl->sock_prot == IPPROTO_MPTCP);
BUG_ON(!proto);
sock_fd = my_socketat(ns, proto->fam->sock_domain, sock_type, proto->sock_prot);
sock_fd = my_socketat(ns, proto->fam->sock_domain, SOCK_STREAM, proto->sock_prot);
/* at first, handle common to all proto families system limits and permission related errors */
if (sock_fd == -1) {

View File

@ -2601,9 +2601,8 @@ int ckch_inst_rebuild(struct ckch_store *ckch_store, struct ckch_inst *ckchi,
fcount = ckchi->crtlist_entry->fcount;
}
if (ckchi->is_server_instance) {
errcode |= ckch_inst_new_load_srv_store(ckch_store->path, ckch_store, new_inst, err, srv_is_quic(ckchi->server));
}
if (ckchi->is_server_instance)
errcode |= ckch_inst_new_load_srv_store(ckch_store->path, ckch_store, new_inst, err);
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);

View File

@ -177,7 +177,7 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *arg)
s = __objt_listener(conn->target)->bind_conf;
#ifdef USE_QUIC
else if (qc)
s = __objt_listener(qc->target)->bind_conf;
s = qc->li->bind_conf;
#endif /* USE_QUIC */
if (!s) {

View File

@ -127,7 +127,7 @@ int ssl_sock_ocsp_stapling_cbk(SSL *ssl, void *arg)
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
/* null if not a listener */
li = objt_listener(qc->target);
li = qc->li;
}
#endif

View File

@ -65,7 +65,6 @@
#include <haproxy/proxy.h>
#include <haproxy/quic_conn.h>
#include <haproxy/quic_openssl_compat.h>
#include <haproxy/quic_ssl.h>
#include <haproxy/quic_tp.h>
#include <haproxy/sample.h>
#include <haproxy/sc_strm.h>
@ -149,6 +148,7 @@ struct global_ssl global_ssl = {
#ifdef HAVE_ACME
.acme_scheduler = 1,
#endif
.renegotiate = SSL_RENEGOTIATE_DFLT,
};
@ -926,7 +926,7 @@ static int ssl_tlsext_ticket_key_cb(SSL *s, unsigned char key_name[16], unsigned
ref = __objt_listener(conn->target)->bind_conf->keys_ref;
#ifdef USE_QUIC
else if (qc)
ref = __objt_listener(qc->target)->bind_conf->keys_ref;
ref = qc->li->bind_conf->keys_ref;
#endif
if (!ref) {
@ -1482,7 +1482,7 @@ int ssl_sock_bind_verifycbk(int ok, X509_STORE_CTX *x_store)
else {
qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
BUG_ON(!qc); /* Must never happen */
bind_conf = __objt_listener(qc->target)->bind_conf;
bind_conf = qc->li->bind_conf;
ctx = qc->xprt_ctx;
}
#endif
@ -3040,20 +3040,6 @@ error:
return errcode;
}
#ifdef USE_QUIC
static inline SSL_CTX *ssl_sock_new_ssl_ctx(int is_quic)
{
if (is_quic)
return ssl_quic_srv_new_ssl_ctx();
else
return SSL_CTX_new(SSLv23_client_method());
}
#else
static inline SSL_CTX *ssl_sock_new_ssl_ctx(int is_quic)
{
return SSL_CTX_new(SSLv23_client_method());
}
#endif
/*
* This function allocate a ckch_inst that will be used on the backend side
@ -3065,7 +3051,7 @@ static inline SSL_CTX *ssl_sock_new_ssl_ctx(int is_quic)
* ERR_WARN if a warning is available into err
*/
int ckch_inst_new_load_srv_store(const char *path, struct ckch_store *ckchs,
struct ckch_inst **ckchi, char **err, int is_quic)
struct ckch_inst **ckchi, char **err)
{
SSL_CTX *ctx;
struct ckch_data *data;
@ -3079,7 +3065,7 @@ int ckch_inst_new_load_srv_store(const char *path, struct ckch_store *ckchs,
data = ckchs->data;
ctx = ssl_sock_new_ssl_ctx(is_quic);
ctx = SSL_CTX_new(SSLv23_client_method());
if (!ctx) {
memprintf(err, "%sunable to allocate SSL context for cert '%s'.\n",
err && *err ? *err : "", path);
@ -3150,8 +3136,7 @@ static int ssl_sock_load_srv_ckchs(const char *path, struct ckch_store *ckchs,
int errcode = 0;
/* we found the ckchs in the tree, we can use it directly */
errcode |= ckch_inst_new_load_srv_store(path, ckchs, ckch_inst, err,
srv_is_quic(server));
errcode |= ckch_inst_new_load_srv_store(path, ckchs, ckch_inst, err);
if (errcode & ERR_CODE)
return errcode;
@ -4414,9 +4399,7 @@ int ssl_sock_prepare_srv_ctx(struct server *srv)
return cfgerr;
}
}
/* The QUIC server xprt has already been set. */
if (srv->use_ssl == 1 && !srv_is_quic(srv))
if (srv->use_ssl == 1)
srv->xprt = &ssl_sock;
if (srv->ssl_ctx.client_crt) {
@ -4443,7 +4426,7 @@ int ssl_sock_prepare_srv_ctx(struct server *srv)
/* The context will be uninitialized if there wasn't any "cert" option
* in the server line. */
if (!ctx) {
ctx = ssl_sock_new_ssl_ctx(srv_is_quic(srv));
ctx = SSL_CTX_new(SSLv23_client_method());
if (!ctx) {
ha_alert("unable to allocate ssl context.\n");
cfgerr++;
@ -4550,6 +4533,12 @@ static int ssl_sock_prepare_srv_ssl_ctx(const struct server *srv, SSL_CTX *ctx)
if (srv->ssl_ctx.options & SRV_SSL_O_NO_TLS_TICKETS)
options |= SSL_OP_NO_TICKET;
if (srv->ssl_ctx.renegotiate == SSL_RENEGOTIATE_OFF)
options |= SSL_OP_NO_RENEGOTIATION;
else if (srv->ssl_ctx.renegotiate == SSL_RENEGOTIATE_ON)
options &= ~SSL_OP_NO_RENEGOTIATION;
SSL_CTX_set_options(ctx, options);
#ifdef SSL_MODE_ASYNC
@ -5117,6 +5106,12 @@ static int ssl_sock_init(struct connection *conn, void **xprt_ctx)
goto err;
SSL_set_connect_state(ctx->ssl);
#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL)
if (srv->ssl_ctx.renegotiate == SSL_RENEGOTIATE_ON)
SSL_set_renegotiate_mode(ctx->ssl, ssl_renegotiate_freely);
#endif
HA_RWLOCK_RDLOCK(SSL_SERVER_LOCK, &srv->ssl_ctx.lock);
if (srv->ssl_ctx.reused_sess[tid].ptr) {
/* let's recreate a session from (ptr,size) and assign

View File

@ -111,25 +111,10 @@ static int quic_conn_unsubscribe(struct connection *conn, void *xprt_ctx, int ev
*/
static int qc_conn_init(struct connection *conn, void **xprt_ctx)
{
int ret = -1;
struct quic_conn *qc = NULL;
struct quic_conn *qc = conn->handle.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. */
if (qc->flags & QUIC_FL_CONN_TID_REBIND)
qc_finalize_tid_rebind(qc);
@ -143,7 +128,7 @@ static int qc_conn_init(struct connection *conn, void **xprt_ctx)
out:
TRACE_LEAVE(QUIC_EV_CONN_NEW, qc);
return ret;
return 0;
}
/* Start the QUIC transport layer */
@ -155,13 +140,8 @@ static int qc_xprt_start(struct connection *conn, void *ctx)
qc = conn->handle.qc;
TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
if (objt_listener(conn->target)) {
/* mux-quic can now be considered ready. */
qc->mux_state = QC_MUX_READY;
}
else {
conn->flags |= CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN;
}
/* mux-quic can now be considered ready. */
qc->mux_state = QC_MUX_READY;
/* Schedule quic-conn to ensure post handshake frames are emitted. This
* is not done for 0-RTT as xprt->start happens before handshake
@ -198,8 +178,6 @@ static struct xprt_ops ssl_quic = {
.start = qc_xprt_start,
.prepare_bind_conf = ssl_sock_prepare_bind_conf,
.destroy_bind_conf = ssl_sock_destroy_bind_conf,
.prepare_srv = ssl_sock_prepare_srv_ctx,
.destroy_srv = ssl_sock_free_srv_ctx,
.get_alpn = ssl_sock_get_alpn,
.get_ssl_sock_ctx = qc_get_ssl_sock_ctx,
.dump_info = qc_xprt_dump_info,