Compare commits
63 Commits
20250526-q
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
577fa44691 | ||
|
830affc17d | ||
|
33cd96a5e9 | ||
|
5a0ae9e9be | ||
|
869fb457ed | ||
|
dc3fb3a731 | ||
|
603afd495b | ||
|
a286d5476b | ||
|
4031bf7432 | ||
|
1efaca8a57 | ||
|
f8d096c05f | ||
|
e8775d51df | ||
|
93b904702f | ||
|
e7f1db0348 | ||
|
a0db93f3d8 | ||
|
044ad3a602 | ||
|
2c3f3eaaed | ||
|
b5067a972c | ||
|
01f011faeb | ||
|
8c573deb9f | ||
|
bf6e576cfd | ||
|
cdcecb9b65 | ||
|
855fd63f90 | ||
|
b9703cf711 | ||
|
f6ef3bbc8a | ||
|
034cf74437 | ||
|
d1cd0bb987 | ||
|
fc90964b55 | ||
|
8c2f2615f4 | ||
|
f085a2f5bf | ||
|
a62098bfb0 | ||
|
e226a7cb79 | ||
|
2d076178c6 | ||
|
b4a9b53515 | ||
|
e27b7b4889 | ||
|
43d88a44f1 | ||
|
266b10b8a4 | ||
|
89d5a59933 | ||
|
f7c0f5ac1b | ||
|
29fb1aee57 | ||
|
9831f596ea | ||
|
52ec3430f2 | ||
|
9c84f64652 | ||
|
f49bbd36b9 | ||
|
1408d94bc4 | ||
|
7c76252d8a | ||
|
1e45690656 | ||
|
a4e1296208 | ||
|
24fc44c44d | ||
|
0e67687ca9 | ||
|
5a711551a2 | ||
|
990c9f95f7 | ||
|
9c751a3cc1 | ||
|
f66b495f8e | ||
|
bdd5e58179 | ||
|
1ecf2e9bab | ||
|
b5525fe759 | ||
|
b2f64af341 | ||
|
6993981cd6 | ||
|
9df380a152 | ||
|
18f9c71041 | ||
|
79445766a3 | ||
|
0d8ecb1edc |
41
CHANGELOG
41
CHANGELOG
@ -1,6 +1,47 @@
|
|||||||
ChangeLog :
|
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
|
2025/05/28 : 3.3-dev0
|
||||||
- MINOR: version: mention that it's development again
|
- MINOR: version: mention that it's development again
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
Configuration Manual
|
Configuration Manual
|
||||||
----------------------
|
----------------------
|
||||||
version 3.3
|
version 3.3
|
||||||
2025/05/28
|
2025/06/11
|
||||||
|
|
||||||
|
|
||||||
This document covers the configuration language as implemented in the version
|
This document covers the configuration language as implemented in the version
|
||||||
@ -9077,11 +9077,14 @@ no option accept-unsafe-violations-in-http-request
|
|||||||
|
|
||||||
* In H1 only, NULL character in header value will be accepted;
|
* In H1 only, NULL character in header value will be accepted;
|
||||||
|
|
||||||
* The list of characters allowed to appear in a URI is well defined by
|
* In H1 only, characters above 127 in the URI will be accepted. The list of
|
||||||
RFC3986, and chars 0-31, 32 (space), 34 ('"'), 60 ('<'), 62 ('>'), 92
|
characters allowed to appear in a URI is well defined by RFC3986, and
|
||||||
('\'), 94 ('^'), 96 ('`'), 123 ('{'), 124 ('|'), 125 ('}'), 127 (delete)
|
chars 0-31, 32 (space), 34 ('"'), 60 ('<'), 62 ('>'), 92 ('\'), 94 ('^'),
|
||||||
and anything above are normally not allowed. But here, in H1 only,
|
96 ('`'), 123 ('{'), 124 ('|'), 125 ('}'), 127 (delete) and anything
|
||||||
HAProxy will only block a number of them (0..32, 127);
|
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.
|
||||||
|
|
||||||
* In H1 and H2, URLs containing fragment references ('#' after the path)
|
* In H1 and H2, URLs containing fragment references ('#' after the path)
|
||||||
will be accepted;
|
will be accepted;
|
||||||
|
@ -282,6 +282,92 @@ static inline void applet_expect_data(struct appctx *appctx)
|
|||||||
se_fl_clr(appctx->sedesc, SE_FL_EXP_NO_DATA);
|
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(). */
|
/* Should only be used via wrappers applet_putchk() / applet_putchk_stress(). */
|
||||||
static inline int _applet_putchk(struct appctx *appctx, struct buffer *chunk,
|
static inline int _applet_putchk(struct appctx *appctx, struct buffer *chunk,
|
||||||
int stress)
|
int stress)
|
||||||
@ -318,8 +404,7 @@ static inline int _applet_putchk(struct appctx *appctx, struct buffer *chunk,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* writes chunk <chunk> into the applet's output buffer if it uses its own
|
/* writes chunk <chunk> into the applet output buffer (see applet_get_outbuf).
|
||||||
* 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,
|
* Returns the number of written bytes on success or -1 on error (lake of space,
|
||||||
* shutdown, invalid call...)
|
* shutdown, invalid call...)
|
||||||
@ -335,8 +420,7 @@ static inline int applet_putchk_stress(struct appctx *appctx, struct buffer *chu
|
|||||||
return _applet_putchk(appctx, chunk, 1);
|
return _applet_putchk(appctx, chunk, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* writes <len> chars from <blk> into the applet's output buffer if it uses its own
|
/* writes <len> chars from <blk> into the applet output buffer (see applet_get_outbuf).
|
||||||
* 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,
|
* Returns the number of written bytes on success or -1 on error (lake of space,
|
||||||
* shutdown, invalid call...)
|
* shutdown, invalid call...)
|
||||||
@ -371,9 +455,8 @@ static inline int applet_putblk(struct appctx *appctx, const char *blk, int len)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* writes chars from <str> up to the trailing zero (excluded) into the applet's
|
/* writes chars from <str> up to the trailing zero (excluded) into the applet
|
||||||
* output buffer if it uses its own bufferx or into the input channel of the
|
* output buffer (see applet_get_outbuf).
|
||||||
* stream attached to this applet.
|
|
||||||
*
|
*
|
||||||
* Returns the number of written bytes on success or -1 on error (lake of space,
|
* Returns the number of written bytes on success or -1 on error (lake of space,
|
||||||
* shutdown, invalid call...)
|
* shutdown, invalid call...)
|
||||||
@ -409,8 +492,7 @@ static inline int applet_putstr(struct appctx *appctx, const char *str)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* writes character <chr> into the applet's output buffer if it uses its own
|
/* writes character <chr> into the applet's output buffer (see applet_get_outbuf).
|
||||||
* 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,
|
* Returns the number of written bytes on success or -1 on error (lake of space,
|
||||||
* shutdown, invalid call...)
|
* shutdown, invalid call...)
|
||||||
@ -446,6 +528,283 @@ static inline int applet_putchr(struct appctx *appctx, char chr)
|
|||||||
return ret;
|
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 */
|
#endif /* _HAPROXY_APPLET_H */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -499,6 +499,7 @@ 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)
|
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);
|
extern void sock_conn_iocb(int);
|
||||||
|
struct tgroup_info *tginfo = &ha_tgroup_info[tgid - 1];
|
||||||
int newstate;
|
int newstate;
|
||||||
|
|
||||||
/* conn_fd_handler should support edge-triggered FDs */
|
/* conn_fd_handler should support edge-triggered FDs */
|
||||||
@ -528,7 +529,7 @@ static inline void fd_insert(int fd, void *owner, void (*iocb)(int fd), int tgid
|
|||||||
BUG_ON(fdtab[fd].state != 0);
|
BUG_ON(fdtab[fd].state != 0);
|
||||||
BUG_ON(tgid < 1 || tgid > MAX_TGROUPS);
|
BUG_ON(tgid < 1 || tgid > MAX_TGROUPS);
|
||||||
|
|
||||||
thread_mask &= tg->threads_enabled;
|
thread_mask &= tginfo->threads_enabled;
|
||||||
BUG_ON(thread_mask == 0);
|
BUG_ON(thread_mask == 0);
|
||||||
|
|
||||||
fd_claim_tgid(fd, tgid);
|
fd_claim_tgid(fd, tgid);
|
||||||
|
@ -202,7 +202,7 @@ struct qcc_app_ops {
|
|||||||
/* Initialize <qcs> stream app context or leave it to NULL if rejected. */
|
/* Initialize <qcs> stream app context or leave it to NULL if rejected. */
|
||||||
int (*attach)(struct qcs *qcs, void *conn_ctx);
|
int (*attach)(struct qcs *qcs, void *conn_ctx);
|
||||||
|
|
||||||
/* Convert received HTTP payload to HTX. */
|
/* Convert received HTTP payload to HTX. Returns amount of decoded bytes from <b> or a negative error code. */
|
||||||
ssize_t (*rcv_buf)(struct qcs *qcs, struct buffer *b, int fin);
|
ssize_t (*rcv_buf)(struct qcs *qcs, struct buffer *b, int fin);
|
||||||
|
|
||||||
/* Convert HTX to HTTP payload for sending. */
|
/* 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 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_ERRL_DONE 0x00000002 /* local error properly handled, connection can be released */
|
||||||
/* unused 0x00000004 */
|
#define QC_CF_IS_BACK 0x00000004 /* backend side */
|
||||||
#define QC_CF_CONN_FULL 0x00000008 /* no stream buffers available on connection */
|
#define QC_CF_CONN_FULL 0x00000008 /* no stream buffers available on connection */
|
||||||
/* unused 0x00000010 */
|
/* unused 0x00000010 */
|
||||||
#define QC_CF_ERR_CONN 0x00000020 /* fatal error reported by transport layer */
|
#define QC_CF_ERR_CONN 0x00000020 /* fatal error reported by transport layer */
|
||||||
|
@ -228,6 +228,9 @@ struct quic_version {
|
|||||||
extern const struct quic_version quic_versions[];
|
extern const struct quic_version quic_versions[];
|
||||||
extern const size_t quic_versions_nb;
|
extern const size_t quic_versions_nb;
|
||||||
extern const struct quic_version *preferred_version;
|
extern const struct quic_version *preferred_version;
|
||||||
|
extern const struct quic_version *quic_version_draft_29;
|
||||||
|
extern const struct quic_version *quic_version_1;
|
||||||
|
extern const struct quic_version *quic_version_2;
|
||||||
|
|
||||||
/* unused: 0x01 */
|
/* unused: 0x01 */
|
||||||
/* Flag the packet number space as requiring an ACK frame to be sent. */
|
/* Flag the packet number space as requiring an ACK frame to be sent. */
|
||||||
@ -305,6 +308,7 @@ struct qcc_app_ops;
|
|||||||
/* Number of received bytes. */ \
|
/* Number of received bytes. */ \
|
||||||
uint64_t rx; \
|
uint64_t rx; \
|
||||||
} bytes; \
|
} bytes; \
|
||||||
|
size_t max_udp_payload; \
|
||||||
/* First DCID used by client on its Initial packet. */ \
|
/* First DCID used by client on its Initial packet. */ \
|
||||||
struct quic_cid odcid; \
|
struct quic_cid odcid; \
|
||||||
/* DCID of our endpoint - not updated when a new DCID is used */ \
|
/* DCID of our endpoint - not updated when a new DCID is used */ \
|
||||||
@ -315,7 +319,7 @@ struct qcc_app_ops;
|
|||||||
* with a connection \
|
* with a connection \
|
||||||
*/ \
|
*/ \
|
||||||
struct eb_root *cids; \
|
struct eb_root *cids; \
|
||||||
struct listener *li; /* only valid for frontend connections */ \
|
enum obj_type *target; \
|
||||||
/* Idle timer task */ \
|
/* Idle timer task */ \
|
||||||
struct task *idle_timer_task; \
|
struct task *idle_timer_task; \
|
||||||
unsigned int idle_expire; \
|
unsigned int idle_expire; \
|
||||||
@ -456,6 +460,7 @@ struct quic_conn_closed {
|
|||||||
#define QUIC_FL_CONN_HPKTNS_DCD (1U << 16) /* Handshake packet number space discarded */
|
#define QUIC_FL_CONN_HPKTNS_DCD (1U << 16) /* Handshake packet number space discarded */
|
||||||
#define QUIC_FL_CONN_PEER_VALIDATED_ADDR (1U << 17) /* Peer address is considered as validated for this connection. */
|
#define QUIC_FL_CONN_PEER_VALIDATED_ADDR (1U << 17) /* Peer address is considered as validated for this connection. */
|
||||||
#define QUIC_FL_CONN_NO_TOKEN_RCVD (1U << 18) /* Client dit not send any token */
|
#define QUIC_FL_CONN_NO_TOKEN_RCVD (1U << 18) /* Client dit not send any token */
|
||||||
|
#define QUIC_FL_CONN_SCID_RECEIVED (1U << 19) /* (client only: first Initial received. */
|
||||||
/* gap here */
|
/* gap here */
|
||||||
#define QUIC_FL_CONN_TO_KILL (1U << 24) /* Unusable connection, to be killed */
|
#define QUIC_FL_CONN_TO_KILL (1U << 24) /* Unusable connection, to be killed */
|
||||||
#define QUIC_FL_CONN_TX_TP_RECEIVED (1U << 25) /* Peer transport parameters have been received (used for the transmitting part) */
|
#define QUIC_FL_CONN_TX_TP_RECEIVED (1U << 25) /* Peer transport parameters have been received (used for the transmitting part) */
|
||||||
|
@ -69,7 +69,8 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
struct quic_connection_id *conn_id,
|
struct quic_connection_id *conn_id,
|
||||||
struct sockaddr_storage *local_addr,
|
struct sockaddr_storage *local_addr,
|
||||||
struct sockaddr_storage *peer_addr,
|
struct sockaddr_storage *peer_addr,
|
||||||
int server, int token, void *owner);
|
int token, void *owner,
|
||||||
|
struct connection *conn);
|
||||||
int quic_build_post_handshake_frames(struct quic_conn *qc);
|
int quic_build_post_handshake_frames(struct quic_conn *qc);
|
||||||
const struct quic_version *qc_supported_version(uint32_t version);
|
const struct quic_version *qc_supported_version(uint32_t version);
|
||||||
int quic_peer_validated_addr(struct quic_conn *qc);
|
int quic_peer_validated_addr(struct quic_conn *qc);
|
||||||
@ -163,6 +164,22 @@ static inline void quic_free_ncbuf(struct ncbuf *ncbuf)
|
|||||||
*ncbuf = NCBUF_NULL;
|
*ncbuf = NCBUF_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return the address of the QUIC counters attached to the proxy of
|
||||||
|
* the owner of the connection whose object type address is <o> for
|
||||||
|
* listener and servers, or NULL for others object type.
|
||||||
|
*/
|
||||||
|
static inline void *qc_counters(enum obj_type *o, const struct stats_module *m)
|
||||||
|
{
|
||||||
|
struct proxy *p;
|
||||||
|
struct listener *l = objt_listener(o);
|
||||||
|
struct server *s = objt_server(o);
|
||||||
|
|
||||||
|
p = l ? l->bind_conf->frontend :
|
||||||
|
s ? s->proxy : NULL;
|
||||||
|
|
||||||
|
return p ? EXTRA_COUNTERS_GET(p->extra_counters_fe, m) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void chunk_frm_appendf(struct buffer *buf, const struct quic_frame *frm);
|
void chunk_frm_appendf(struct buffer *buf, const struct quic_frame *frm);
|
||||||
void quic_set_connection_close(struct quic_conn *qc, const struct quic_err err);
|
void quic_set_connection_close(struct quic_conn *qc, const struct quic_err err);
|
||||||
void quic_set_tls_alert(struct quic_conn *qc, int alert);
|
void quic_set_tls_alert(struct quic_conn *qc, int alert);
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
#include <haproxy/quic_rx-t.h>
|
#include <haproxy/quic_rx-t.h>
|
||||||
|
|
||||||
int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
|
int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
|
||||||
struct listener *li);
|
enum obj_type *obj_type);
|
||||||
int qc_treat_rx_pkts(struct quic_conn *qc);
|
int qc_treat_rx_pkts(struct quic_conn *qc);
|
||||||
int qc_parse_hd_form(struct quic_rx_packet *pkt,
|
int qc_parse_hd_form(struct quic_rx_packet *pkt,
|
||||||
unsigned char **pos, const unsigned char *end);
|
unsigned char **pos, const unsigned char *end);
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include <haproxy/connection-t.h>
|
#include <haproxy/connection-t.h>
|
||||||
#include <haproxy/fd-t.h>
|
#include <haproxy/fd-t.h>
|
||||||
#include <haproxy/listener-t.h>
|
#include <haproxy/listener-t.h>
|
||||||
|
#include <haproxy/obj_type.h>
|
||||||
#include <haproxy/quic_conn-t.h>
|
#include <haproxy/quic_conn-t.h>
|
||||||
#include <haproxy/quic_sock-t.h>
|
#include <haproxy/quic_sock-t.h>
|
||||||
|
|
||||||
@ -78,7 +79,8 @@ static inline char qc_test_fd(struct quic_conn *qc)
|
|||||||
*/
|
*/
|
||||||
static inline int qc_fd(struct quic_conn *qc)
|
static inline int qc_fd(struct quic_conn *qc)
|
||||||
{
|
{
|
||||||
return qc_test_fd(qc) ? qc->fd : qc->li->rx.fd;
|
/* TODO: check this: For backends, qc->fd is always initialized */
|
||||||
|
return qc_test_fd(qc) ? qc->fd : __objt_listener(qc->target)->rx.fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try to increment <l> handshake current counter. If listener limit is
|
/* Try to increment <l> handshake current counter. If listener limit is
|
||||||
|
@ -34,7 +34,8 @@
|
|||||||
#include <haproxy/ssl_sock-t.h>
|
#include <haproxy/ssl_sock-t.h>
|
||||||
|
|
||||||
int ssl_quic_initial_ctx(struct bind_conf *bind_conf);
|
int ssl_quic_initial_ctx(struct bind_conf *bind_conf);
|
||||||
int qc_alloc_ssl_sock_ctx(struct quic_conn *qc);
|
SSL_CTX *ssl_quic_srv_new_ssl_ctx(void);
|
||||||
|
int qc_alloc_ssl_sock_ctx(struct quic_conn *qc, struct connection *conn);
|
||||||
int qc_ssl_provide_all_quic_data(struct quic_conn *qc, struct ssl_sock_ctx *ctx);
|
int qc_ssl_provide_all_quic_data(struct quic_conn *qc, struct ssl_sock_ctx *ctx);
|
||||||
int quic_ssl_set_tls_cbs(SSL *ssl);
|
int quic_ssl_set_tls_cbs(SSL *ssl);
|
||||||
|
|
||||||
|
@ -26,6 +26,9 @@ int qc_lstnr_params_init(struct quic_conn *qc,
|
|||||||
const unsigned char *dcid, size_t dcidlen,
|
const unsigned char *dcid, size_t dcidlen,
|
||||||
const unsigned char *scid, size_t scidlen,
|
const unsigned char *scid, size_t scidlen,
|
||||||
const struct quic_cid *token_odcid);
|
const struct quic_cid *token_odcid);
|
||||||
|
void qc_srv_params_init(struct quic_conn *qc,
|
||||||
|
const struct quic_transport_params *srv_params,
|
||||||
|
const unsigned char *scid, size_t scidlen);
|
||||||
|
|
||||||
/* Dump <cid> transport parameter connection ID value if present (non null length).
|
/* Dump <cid> transport parameter connection ID value if present (non null length).
|
||||||
* Used only for debugging purposes.
|
* Used only for debugging purposes.
|
||||||
|
@ -478,6 +478,9 @@ struct server {
|
|||||||
char *alpn_str; /* ALPN protocol string */
|
char *alpn_str; /* ALPN protocol string */
|
||||||
int alpn_len; /* ALPN protocol string length */
|
int alpn_len; /* ALPN protocol string length */
|
||||||
} ssl_ctx;
|
} ssl_ctx;
|
||||||
|
#ifdef USE_QUIC
|
||||||
|
struct quic_transport_params quic_params; /* QUIC transport parameters */
|
||||||
|
#endif
|
||||||
struct resolv_srvrq *srvrq; /* Pointer representing the DNS SRV requeest, if any */
|
struct resolv_srvrq *srvrq; /* Pointer representing the DNS SRV requeest, if any */
|
||||||
struct list srv_rec_item; /* to attach server to a srv record item */
|
struct list srv_rec_item; /* to attach server to a srv record item */
|
||||||
struct list ip_rec_item; /* to attach server to a A or AAAA record item */
|
struct list ip_rec_item; /* to attach server to a A or AAAA record item */
|
||||||
|
@ -343,6 +343,16 @@ static inline void srv_detach(struct server *srv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int srv_is_quic(const struct server *srv)
|
||||||
|
{
|
||||||
|
#ifdef USE_QUIC
|
||||||
|
return srv->addr_type.proto_type == PROTO_TYPE_DGRAM &&
|
||||||
|
srv->addr_type.xprt_type == PROTO_TYPE_STREAM;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _HAPROXY_SERVER_H */
|
#endif /* _HAPROXY_SERVER_H */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -28,9 +28,11 @@
|
|||||||
#include <haproxy/api.h>
|
#include <haproxy/api.h>
|
||||||
#include <haproxy/connection-t.h>
|
#include <haproxy/connection-t.h>
|
||||||
#include <haproxy/listener-t.h>
|
#include <haproxy/listener-t.h>
|
||||||
|
#include <haproxy/protocol-t.h>
|
||||||
#include <haproxy/sock-t.h>
|
#include <haproxy/sock-t.h>
|
||||||
|
|
||||||
int sock_create_server_socket(struct connection *conn, struct proxy *be, int *stream_err);
|
int sock_create_server_socket(struct connection *conn, struct proxy *be,
|
||||||
|
enum proto_type proto_type, int sock_type, int *stream_err);
|
||||||
void sock_enable(struct receiver *rx);
|
void sock_enable(struct receiver *rx);
|
||||||
void sock_disable(struct receiver *rx);
|
void sock_disable(struct receiver *rx);
|
||||||
void sock_unbind(struct receiver *rx);
|
void sock_unbind(struct receiver *rx);
|
||||||
|
@ -62,7 +62,7 @@ struct ckch_inst *ckch_inst_new();
|
|||||||
int ckch_inst_new_load_store(const char *path, struct ckch_store *ckchs, struct bind_conf *bind_conf,
|
int ckch_inst_new_load_store(const char *path, struct ckch_store *ckchs, struct bind_conf *bind_conf,
|
||||||
struct ssl_bind_conf *ssl_conf, char **sni_filter, int fcount, int is_default, struct ckch_inst **ckchi, char **err);
|
struct ssl_bind_conf *ssl_conf, char **sni_filter, int fcount, int is_default, struct ckch_inst **ckchi, char **err);
|
||||||
int ckch_inst_new_load_srv_store(const char *path, struct ckch_store *ckchs,
|
int ckch_inst_new_load_srv_store(const char *path, struct ckch_store *ckchs,
|
||||||
struct ckch_inst **ckchi, char **err);
|
struct ckch_inst **ckchi, char **err, int is_quic);
|
||||||
int ckch_inst_rebuild(struct ckch_store *ckch_store, struct ckch_inst *ckchi,
|
int ckch_inst_rebuild(struct ckch_store *ckch_store, struct ckch_inst *ckchi,
|
||||||
struct ckch_inst **new_inst, char **err);
|
struct ckch_inst **new_inst, char **err);
|
||||||
|
|
||||||
|
@ -1992,7 +1992,9 @@ int connect_server(struct stream *s)
|
|||||||
/* set the correct protocol on the output stream connector */
|
/* set the correct protocol on the output stream connector */
|
||||||
|
|
||||||
if (srv) {
|
if (srv) {
|
||||||
if (conn_prepare(srv_conn, protocol_lookup(srv_conn->dst->ss_family, PROTO_TYPE_STREAM, srv->alt_proto), srv->xprt)) {
|
struct protocol *proto = protocol_lookup(srv_conn->dst->ss_family, srv->addr_type.proto_type, srv->alt_proto);
|
||||||
|
|
||||||
|
if (conn_prepare(srv_conn, proto, srv->xprt)) {
|
||||||
conn_free(srv_conn);
|
conn_free(srv_conn);
|
||||||
return SF_ERR_INTERNAL;
|
return SF_ERR_INTERNAL;
|
||||||
}
|
}
|
||||||
@ -2018,9 +2020,13 @@ int connect_server(struct stream *s)
|
|||||||
srv_conn->ctx = s->scb;
|
srv_conn->ctx = s->scb;
|
||||||
|
|
||||||
#if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
|
#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 ||
|
if (!srv ||
|
||||||
(srv->use_ssl != 1 || (!(srv->ssl_ctx.alpn_str) && !(srv->ssl_ctx.npn_str)) ||
|
(srv->use_ssl != 1 || (!(srv->ssl_ctx.alpn_str) && !(srv->ssl_ctx.npn_str)) ||
|
||||||
srv->mux_proto || !IS_HTX_STRM(s)))
|
!IS_HTX_STRM(s)))
|
||||||
#endif
|
#endif
|
||||||
init_mux = 1;
|
init_mux = 1;
|
||||||
|
|
||||||
|
@ -3767,6 +3767,8 @@ out_uri_auth_compat:
|
|||||||
((newsrv->flags & SRV_F_DEFSRV_USE_SSL) && newsrv->use_ssl != 1)) {
|
((newsrv->flags & SRV_F_DEFSRV_USE_SSL) && newsrv->use_ssl != 1)) {
|
||||||
if (xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->prepare_srv)
|
if (xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->prepare_srv)
|
||||||
cfgerr += xprt_get(XPRT_SSL)->prepare_srv(newsrv);
|
cfgerr += xprt_get(XPRT_SSL)->prepare_srv(newsrv);
|
||||||
|
else if (xprt_get(XPRT_QUIC) && xprt_get(XPRT_QUIC)->prepare_srv)
|
||||||
|
cfgerr += xprt_get(XPRT_QUIC)->prepare_srv(newsrv);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((newsrv->flags & SRV_F_FASTOPEN) &&
|
if ((newsrv->flags & SRV_F_FASTOPEN) &&
|
||||||
@ -4113,6 +4115,13 @@ out_uri_auth_compat:
|
|||||||
int mode = conn_pr_mode_to_proto_mode(curproxy->mode);
|
int mode = conn_pr_mode_to_proto_mode(curproxy->mode);
|
||||||
const struct mux_proto_list *mux_ent;
|
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)
|
if (!newsrv->mux_proto)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -1423,7 +1423,8 @@ static int cli_io_handler_show_fd(struct appctx *appctx)
|
|||||||
#if defined(USE_QUIC)
|
#if defined(USE_QUIC)
|
||||||
else if (fdt.iocb == quic_conn_sock_fd_iocb) {
|
else if (fdt.iocb == quic_conn_sock_fd_iocb) {
|
||||||
qc = fdtab[fd].owner;
|
qc = fdtab[fd].owner;
|
||||||
li = qc ? qc->li : NULL;
|
li = qc ? objt_listener(qc->target) : NULL;
|
||||||
|
sv = qc ? objt_server(qc->target) : NULL;
|
||||||
xprt_ctx = qc ? qc->xprt_ctx : NULL;
|
xprt_ctx = qc ? qc->xprt_ctx : NULL;
|
||||||
conn = qc ? qc->conn : NULL;
|
conn = qc ? qc->conn : NULL;
|
||||||
xprt = conn ? conn->xprt : NULL; // in fact it's &ssl_quic
|
xprt = conn ? conn->xprt : NULL; // in fact it's &ssl_quic
|
||||||
|
45
src/h3.c
45
src/h3.c
@ -36,6 +36,7 @@
|
|||||||
#include <haproxy/qmux_http.h>
|
#include <haproxy/qmux_http.h>
|
||||||
#include <haproxy/qpack-dec.h>
|
#include <haproxy/qpack-dec.h>
|
||||||
#include <haproxy/qpack-enc.h>
|
#include <haproxy/qpack-enc.h>
|
||||||
|
#include <haproxy/quic_conn.h>
|
||||||
#include <haproxy/quic_enc.h>
|
#include <haproxy/quic_enc.h>
|
||||||
#include <haproxy/quic_fctl.h>
|
#include <haproxy/quic_fctl.h>
|
||||||
#include <haproxy/quic_frame.h>
|
#include <haproxy/quic_frame.h>
|
||||||
@ -1384,9 +1385,13 @@ 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
|
/* 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.
|
* <qcs>. If <fin> is set, it indicates that no more data will arrive after.
|
||||||
*
|
*
|
||||||
* Returns the count of consumed bytes or a negative error code. If 0 is
|
* It may be necessary to call this function with an empty input buffer to
|
||||||
* returned, stream data is incomplete, decoding should be call again later
|
* signal a standalone FIN.
|
||||||
* with more content.
|
*
|
||||||
|
* 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.
|
||||||
*/
|
*/
|
||||||
static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
||||||
{
|
{
|
||||||
@ -1686,6 +1691,13 @@ static int h3_encode_header(struct buffer *buf,
|
|||||||
return qpack_encode_header(buf, n, v_strip);
|
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)
|
static int h3_resp_headers_send(struct qcs *qcs, struct htx *htx)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
@ -1840,15 +1852,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
|
/* Convert a series of HTX trailer blocks from <htx> buffer into <qcs> buffer
|
||||||
* as a H3 HEADERS frame. H3 forbidden trailers are skipped. HTX trailer blocks
|
* as a HTTP/3 HEADERS frame. Forbidden trailers are skipped. HTX trailer
|
||||||
* are removed from <htx> until EOT is found and itself removed.
|
* blocks are removed from <htx> up to end-of-trailer included.
|
||||||
*
|
*
|
||||||
* If only a EOT HTX block is present without trailer, no H3 frame is produced.
|
* 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
|
* Caller is responsible to emit an empty QUIC STREAM frame to signal the end
|
||||||
* of the stream.
|
* of the stream.
|
||||||
*
|
*
|
||||||
* Returns the size of HTX blocks removed. A negative error code is returned in
|
* Returns the amount of consumed bytes from <htx> buffer or a negative error
|
||||||
* case of a fatal error which should caused a connection closure.
|
* code.
|
||||||
*/
|
*/
|
||||||
static int h3_resp_trailers_send(struct qcs *qcs, struct htx *htx)
|
static int h3_resp_trailers_send(struct qcs *qcs, struct htx *htx)
|
||||||
{
|
{
|
||||||
@ -2020,9 +2032,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
|
* underlying HTX area via <buf> as this will be used if zero-copy can be
|
||||||
* performed.
|
* performed.
|
||||||
*
|
*
|
||||||
* Returns the total bytes of encoded HTTP/3 payload. This corresponds to the
|
* Returns the amount of consumed bytes from <htx> buffer, which corresponds to
|
||||||
* total bytes of HTX block removed. A negative error code is returned in case
|
* the length sum of encoded frames payload. A negative error code is returned
|
||||||
* of a fatal error which should caused a connection closure.
|
* in case of a fatal error which should caused a connection closure.
|
||||||
*/
|
*/
|
||||||
static int h3_resp_data_send(struct qcs *qcs, struct htx *htx,
|
static int h3_resp_data_send(struct qcs *qcs, struct htx *htx,
|
||||||
struct buffer *buf, size_t count)
|
struct buffer *buf, size_t count)
|
||||||
@ -2147,6 +2159,14 @@ static int h3_resp_data_send(struct qcs *qcs, struct htx *htx,
|
|||||||
return -1;
|
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)
|
static size_t h3_snd_buf(struct qcs *qcs, struct buffer *buf, size_t count)
|
||||||
{
|
{
|
||||||
size_t total = 0;
|
size_t total = 0;
|
||||||
@ -2513,7 +2533,6 @@ static int h3_send_goaway(struct h3c *h3c)
|
|||||||
static int h3_init(struct qcc *qcc)
|
static int h3_init(struct qcc *qcc)
|
||||||
{
|
{
|
||||||
struct h3c *h3c;
|
struct h3c *h3c;
|
||||||
const struct listener *li = __objt_listener(qcc->conn->target);
|
|
||||||
|
|
||||||
TRACE_ENTER(H3_EV_H3C_NEW, qcc->conn);
|
TRACE_ENTER(H3_EV_H3C_NEW, qcc->conn);
|
||||||
|
|
||||||
@ -2530,9 +2549,7 @@ static int h3_init(struct qcc *qcc)
|
|||||||
h3c->id_goaway = 0;
|
h3c->id_goaway = 0;
|
||||||
|
|
||||||
qcc->ctx = h3c;
|
qcc->ctx = h3c;
|
||||||
h3c->prx_counters =
|
h3c->prx_counters = qc_counters(qcc->conn->target, &h3_stats_module);
|
||||||
EXTRA_COUNTERS_GET(li->bind_conf->frontend->extra_counters_fe,
|
|
||||||
&h3_stats_module);
|
|
||||||
LIST_INIT(&h3c->buf_wait.list);
|
LIST_INIT(&h3c->buf_wait.list);
|
||||||
|
|
||||||
TRACE_LEAVE(H3_EV_H3C_NEW, qcc->conn);
|
TRACE_LEAVE(H3_EV_H3C_NEW, qcc->conn);
|
||||||
|
106
src/hlua.c
106
src/hlua.c
@ -5299,48 +5299,20 @@ __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)
|
__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 hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
|
||||||
struct stconn *sc = appctx_sc(luactx->appctx);
|
|
||||||
int ret;
|
int ret;
|
||||||
const char *blk1;
|
const char *blk1 = NULL;
|
||||||
size_t len1;
|
size_t len1 = 0;
|
||||||
const char *blk2;
|
const char *blk2 = NULL;
|
||||||
size_t len2;
|
size_t len2 = 0;
|
||||||
|
|
||||||
/* Read the maximum amount of data available. */
|
/* Read the maximum amount of data available. */
|
||||||
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS) {
|
ret = applet_getline_nc(luactx->appctx, &blk1, &len1, &blk2, &len2);
|
||||||
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. */
|
/* Data not yet available. return yield. */
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
applet_need_more_data(luactx->appctx);
|
applet_need_more_data(luactx->appctx);
|
||||||
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_tcp_getline_yield, TICK_ETERNITY, 0));
|
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. */
|
/* End of data: commit the total strings and return. */
|
||||||
@ -5355,13 +5327,10 @@ __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. */
|
/* don't check the max length read and don't check. */
|
||||||
luaL_addlstring(&luactx->b, blk1, len1);
|
luaL_addlstring(&luactx->b, blk1, len1);
|
||||||
luaL_addlstring(&luactx->b, blk2, len2);
|
if (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);
|
luaL_pushresult(&luactx->b);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -5383,23 +5352,16 @@ __LJMP static int hlua_applet_tcp_getline(lua_State *L)
|
|||||||
__LJMP static int hlua_applet_tcp_recv_try(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 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));
|
size_t len = MAY_LJMP(luaL_checkinteger(L, 2));
|
||||||
int exp_date = MAY_LJMP(luaL_checkinteger(L, 3));
|
int exp_date = MAY_LJMP(luaL_checkinteger(L, 3));
|
||||||
int ret;
|
int ret;
|
||||||
const char *blk1;
|
const char *blk1 = NULL;
|
||||||
size_t len1;
|
size_t len1 = 0;
|
||||||
const char *blk2;
|
const char *blk2 = NULL;
|
||||||
size_t len2;
|
size_t len2 = 0;
|
||||||
|
|
||||||
/* Read the maximum amount of data available. */
|
/* Read the maximum amount of data available. */
|
||||||
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS) {
|
ret = applet_getblk_nc(luactx->appctx, &blk1, &len1, &blk2, &len2);
|
||||||
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. */
|
/* Data not yet available. return yield. */
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
@ -5430,11 +5392,9 @@ __LJMP static int hlua_applet_tcp_recv_try(lua_State *L)
|
|||||||
* the end of data stream.
|
* the end of data stream.
|
||||||
*/
|
*/
|
||||||
luaL_addlstring(&luactx->b, blk1, len1);
|
luaL_addlstring(&luactx->b, blk1, len1);
|
||||||
luaL_addlstring(&luactx->b, blk2, len2);
|
if (len2)
|
||||||
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS)
|
luaL_addlstring(&luactx->b, blk2, len2);
|
||||||
b_del(&luactx->appctx->inbuf, len1 + len2);
|
applet_skip_input(luactx->appctx, len1+len2);
|
||||||
else
|
|
||||||
co_skip(sc_oc(sc), len1 + len2);
|
|
||||||
|
|
||||||
if (tick_is_expired(exp_date, now_ms)) {
|
if (tick_is_expired(exp_date, now_ms)) {
|
||||||
/* return the result. */
|
/* return the result. */
|
||||||
@ -5454,15 +5414,14 @@ __LJMP static int hlua_applet_tcp_recv_try(lua_State *L)
|
|||||||
len -= len1;
|
len -= len1;
|
||||||
|
|
||||||
/* Copy the second block. */
|
/* Copy the second block. */
|
||||||
if (len2 > len)
|
if (len2) {
|
||||||
len2 = len;
|
if (len2 > len)
|
||||||
luaL_addlstring(&luactx->b, blk2, len2);
|
len2 = len;
|
||||||
len -= len2;
|
luaL_addlstring(&luactx->b, blk2, len2);
|
||||||
|
len -= len2;
|
||||||
|
}
|
||||||
|
|
||||||
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS)
|
applet_skip_input(luactx->appctx, len1+len2);
|
||||||
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 there is no other data available, yield waiting for new data. */
|
||||||
if (len > 0 && !tick_is_expired(exp_date, now_ms)) {
|
if (len > 0 && !tick_is_expired(exp_date, now_ms)) {
|
||||||
@ -5571,15 +5530,10 @@ __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));
|
struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
|
||||||
const char *str = MAY_LJMP(luaL_checklstring(L, 2, &len));
|
const char *str = MAY_LJMP(luaL_checklstring(L, 2, &len));
|
||||||
int l = MAY_LJMP(luaL_checkinteger(L, 3));
|
int l = MAY_LJMP(luaL_checkinteger(L, 3));
|
||||||
struct stconn *sc = appctx_sc(luactx->appctx);
|
|
||||||
struct channel *chn = sc_ic(sc);
|
|
||||||
int max;
|
int max;
|
||||||
|
|
||||||
/* Get the max amount of data which can be written */
|
/* Get the max amount of data which can be written */
|
||||||
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS)
|
max = applet_output_room(luactx->appctx);
|
||||||
max = b_room(&luactx->appctx->outbuf);
|
|
||||||
else
|
|
||||||
max = channel_recv_max(chn);
|
|
||||||
|
|
||||||
if (max > (len - l))
|
if (max > (len - l))
|
||||||
max = len - l;
|
max = len - l;
|
||||||
@ -5596,10 +5550,7 @@ __LJMP static int hlua_applet_tcp_send_yield(lua_State *L, int status, lua_KCont
|
|||||||
* applet, and returns a yield.
|
* applet, and returns a yield.
|
||||||
*/
|
*/
|
||||||
if (l < len) {
|
if (l < len) {
|
||||||
if (luactx->appctx->flags & APPCTX_FL_INOUT_BUFS)
|
applet_need_room(luactx->appctx, applet_output_room(luactx->appctx) + 1);
|
||||||
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));
|
MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_tcp_send_yield, TICK_ETERNITY, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11261,10 +11212,7 @@ out:
|
|||||||
if (yield)
|
if (yield)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (ctx->flags & APPCTX_FL_INOUT_BUFS)
|
applet_reset_input(ctx);
|
||||||
b_reset(&ctx->inbuf);
|
|
||||||
else
|
|
||||||
co_skip(sc_oc(sc), co_data(sc_oc(sc)));
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
@ -6,13 +6,15 @@
|
|||||||
#include <haproxy/dynbuf.h>
|
#include <haproxy/dynbuf.h>
|
||||||
#include <haproxy/htx.h>
|
#include <haproxy/htx.h>
|
||||||
#include <haproxy/http.h>
|
#include <haproxy/http.h>
|
||||||
|
#include <haproxy/istbuf.h>
|
||||||
#include <haproxy/mux_quic.h>
|
#include <haproxy/mux_quic.h>
|
||||||
#include <haproxy/qmux_http.h>
|
#include <haproxy/qmux_http.h>
|
||||||
#include <haproxy/qmux_trace.h>
|
#include <haproxy/qmux_trace.h>
|
||||||
#include <haproxy/quic_utils.h>
|
#include <haproxy/quic_utils.h>
|
||||||
#include <haproxy/trace.h>
|
#include <haproxy/trace.h>
|
||||||
|
|
||||||
static ssize_t hq_interop_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
/* HTTP/0.9 request -> HTX. */
|
||||||
|
static ssize_t hq_interop_rcv_buf_req(struct qcs *qcs, struct buffer *b, int fin)
|
||||||
{
|
{
|
||||||
struct htx *htx;
|
struct htx *htx;
|
||||||
struct htx_sl *sl;
|
struct htx_sl *sl;
|
||||||
@ -92,12 +94,74 @@ static ssize_t hq_interop_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
|
|||||||
return b_data(b);
|
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,
|
static size_t hq_interop_snd_buf(struct qcs *qcs, struct buffer *buf,
|
||||||
size_t count)
|
size_t count)
|
||||||
{
|
{
|
||||||
enum htx_blk_type btype;
|
enum htx_blk_type btype;
|
||||||
struct htx *htx = NULL;
|
struct htx *htx = NULL;
|
||||||
struct htx_blk *blk;
|
struct htx_blk *blk;
|
||||||
|
struct htx_sl *sl = NULL;
|
||||||
int32_t idx;
|
int32_t idx;
|
||||||
uint32_t bsize, fsize;
|
uint32_t bsize, fsize;
|
||||||
struct buffer *res = NULL;
|
struct buffer *res = NULL;
|
||||||
@ -113,9 +177,25 @@ static size_t hq_interop_snd_buf(struct qcs *qcs, struct buffer *buf,
|
|||||||
btype = htx_get_blk_type(blk);
|
btype = htx_get_blk_type(blk);
|
||||||
fsize = bsize = htx_get_blksz(blk);
|
fsize = bsize = htx_get_blksz(blk);
|
||||||
|
|
||||||
BUG_ON(btype == HTX_BLK_REQ_SL);
|
|
||||||
|
|
||||||
switch (btype) {
|
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:
|
case HTX_BLK_DATA:
|
||||||
res = qcc_get_stream_txbuf(qcs, &err, 0);
|
res = qcc_get_stream_txbuf(qcs, &err, 0);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
|
@ -1091,14 +1091,15 @@ void listener_accept(struct listener *l)
|
|||||||
#endif
|
#endif
|
||||||
if (p && p->fe_sps_lim) {
|
if (p && p->fe_sps_lim) {
|
||||||
int max = 0;
|
int max = 0;
|
||||||
|
int it;
|
||||||
|
|
||||||
for (int it = 0; it < global.nbtgroups; it++)
|
for (it = 0; it < global.nbtgroups; it++)
|
||||||
max += freq_ctr_remain(&p->fe_counters.shared->tg[it]->sess_per_sec, p->fe_sps_lim, 0);
|
max += freq_ctr_remain(&p->fe_counters.shared->tg[it]->sess_per_sec, p->fe_sps_lim, 0);
|
||||||
|
|
||||||
if (unlikely(!max)) {
|
if (unlikely(!max)) {
|
||||||
unsigned int min_wait = 0;
|
unsigned int min_wait = 0;
|
||||||
|
|
||||||
for (int it = 0; it < global.nbtgroups; it++) {
|
for (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);
|
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)
|
if (!it || cur_wait < min_wait)
|
||||||
min_wait = cur_wait;
|
min_wait = cur_wait;
|
||||||
|
@ -134,6 +134,7 @@ static struct qcs *qcs_new(struct qcc *qcc, uint64_t id, enum qcs_type type)
|
|||||||
|
|
||||||
qcs->stream = NULL;
|
qcs->stream = NULL;
|
||||||
qcs->qcc = qcc;
|
qcs->qcc = qcc;
|
||||||
|
qcs->sd = NULL;
|
||||||
qcs->flags = QC_SF_NONE;
|
qcs->flags = QC_SF_NONE;
|
||||||
qcs->st = QC_SS_IDLE;
|
qcs->st = QC_SS_IDLE;
|
||||||
qcs->ctx = NULL;
|
qcs->ctx = NULL;
|
||||||
@ -3114,10 +3115,10 @@ static void qcc_shutdown(struct qcc *qcc)
|
|||||||
TRACE_LEAVE(QMUX_EV_QCC_END, qcc->conn);
|
TRACE_LEAVE(QMUX_EV_QCC_END, qcc->conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Loop through all qcs from <qcc>. Report error on stream endpoint if
|
/* Loop through all qcs from <qcc> and wake their associated data layer if
|
||||||
* connection on error and wake them.
|
* still active. Also report error on it if connection is already in error.
|
||||||
*/
|
*/
|
||||||
static int qcc_wake_some_streams(struct qcc *qcc)
|
static void qcc_wake_streams(struct qcc *qcc)
|
||||||
{
|
{
|
||||||
struct qcs *qcs;
|
struct qcs *qcs;
|
||||||
struct eb64_node *node;
|
struct eb64_node *node;
|
||||||
@ -3134,11 +3135,10 @@ static int qcc_wake_some_streams(struct qcc *qcc)
|
|||||||
if (qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL)) {
|
if (qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL)) {
|
||||||
TRACE_POINT(QMUX_EV_QCC_WAKE, qcc->conn, qcs);
|
TRACE_POINT(QMUX_EV_QCC_WAKE, qcc->conn, qcs);
|
||||||
se_fl_set_error(qcs->sd);
|
se_fl_set_error(qcs->sd);
|
||||||
qcs_alert(qcs);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
qcs_alert(qcs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Conduct operations which should be made for <qcc> connection after
|
/* 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. */
|
/* Report error if set on stream endpoint layer. */
|
||||||
if (qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL))
|
if (qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL))
|
||||||
qcc_wake_some_streams(qcc);
|
qcc_wake_streams(qcc);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (qcc_is_dead(qcc))
|
if (qcc_is_dead(qcc))
|
||||||
@ -3406,6 +3406,7 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
|
|||||||
{
|
{
|
||||||
struct qcc *qcc;
|
struct qcc *qcc;
|
||||||
struct quic_transport_params *lparams, *rparams;
|
struct quic_transport_params *lparams, *rparams;
|
||||||
|
void *conn_ctx = conn->ctx;
|
||||||
|
|
||||||
TRACE_ENTER(QMUX_EV_QCC_NEW);
|
TRACE_ENTER(QMUX_EV_QCC_NEW);
|
||||||
|
|
||||||
@ -3418,7 +3419,7 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
|
|||||||
_qcc_init(qcc);
|
_qcc_init(qcc);
|
||||||
conn->ctx = qcc;
|
conn->ctx = qcc;
|
||||||
qcc->nb_hreq = qcc->nb_sc = 0;
|
qcc->nb_hreq = qcc->nb_sc = 0;
|
||||||
qcc->flags = 0;
|
qcc->flags = conn_is_back(conn) ? QC_CF_IS_BACK : 0;
|
||||||
qcc->app_st = QCC_APP_ST_NULL;
|
qcc->app_st = QCC_APP_ST_NULL;
|
||||||
qcc->glitches = 0;
|
qcc->glitches = 0;
|
||||||
qcc->err = quic_err_transport(QC_ERR_NO_ERROR);
|
qcc->err = quic_err_transport(QC_ERR_NO_ERROR);
|
||||||
@ -3531,27 +3532,48 @@ static int qmux_init(struct connection *conn, struct proxy *prx,
|
|||||||
if (qcc->app_ops == &h3_ops && !conn_is_back(conn))
|
if (qcc->app_ops == &h3_ops && !conn_is_back(conn))
|
||||||
proxy_inc_fe_cum_sess_ver_ctr(sess->listener, prx, 3);
|
proxy_inc_fe_cum_sess_ver_ctr(sess->listener, prx, 3);
|
||||||
|
|
||||||
/* Register conn for idle front closing. This is done once everything is allocated. */
|
if (!conn_is_back(conn)) {
|
||||||
if (!conn_is_back(conn))
|
/* Register conn for idle front closing. */
|
||||||
LIST_APPEND(&mux_stopping_data[tid].list, &conn->stopping_list);
|
LIST_APPEND(&mux_stopping_data[tid].list, &conn->stopping_list);
|
||||||
|
|
||||||
/* init read cycle */
|
/* init read cycle */
|
||||||
tasklet_wakeup(qcc->wait_event.tasklet);
|
tasklet_wakeup(qcc->wait_event.tasklet);
|
||||||
|
|
||||||
/* MUX is initialized before QUIC handshake completion if early data
|
/* MUX is initialized before QUIC handshake completion if early data
|
||||||
* received. Flag connection to delay stream processing if
|
* received. Flag connection to delay stream processing if
|
||||||
* wait-for-handshake is active.
|
* wait-for-handshake is active.
|
||||||
*/
|
*/
|
||||||
if (conn->handle.qc->state < QUIC_HS_ST_COMPLETE) {
|
if (conn->handle.qc->state < QUIC_HS_ST_COMPLETE) {
|
||||||
if (!(conn->flags & CO_FL_EARLY_SSL_HS)) {
|
if (!(conn->flags & CO_FL_EARLY_SSL_HS)) {
|
||||||
TRACE_STATE("flag connection with early data", QMUX_EV_QCC_WAKE, conn);
|
TRACE_STATE("flag connection with early data", QMUX_EV_QCC_WAKE, conn);
|
||||||
conn->flags |= CO_FL_EARLY_SSL_HS;
|
conn->flags |= CO_FL_EARLY_SSL_HS;
|
||||||
/* subscribe for handshake completion */
|
/* subscribe for handshake completion */
|
||||||
conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV,
|
conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV,
|
||||||
&qcc->wait_event);
|
&qcc->wait_event);
|
||||||
qcc->flags |= QC_CF_WAIT_HS;
|
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);
|
TRACE_LEAVE(QMUX_EV_QCC_NEW, conn);
|
||||||
return 0;
|
return 0;
|
||||||
@ -3657,10 +3679,12 @@ 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);
|
TRACE_STATE("report end-of-input", QMUX_EV_STRM_RECV, qcc->conn, qcs);
|
||||||
se_fl_set(qcs->sd, SE_FL_EOI);
|
se_fl_set(qcs->sd, SE_FL_EOI);
|
||||||
|
|
||||||
/* If request EOM is reported to the upper layer, it means the
|
if (!(qcc->flags & QC_CF_IS_BACK)) {
|
||||||
* QCS now expects data from the opposite side.
|
/* 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);
|
*/
|
||||||
|
se_expect_data(qcs->sd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set end-of-stream on read closed. */
|
/* Set end-of-stream on read closed. */
|
||||||
@ -3942,7 +3966,11 @@ static int qmux_wake(struct connection *conn)
|
|||||||
goto release;
|
goto release;
|
||||||
}
|
}
|
||||||
|
|
||||||
qcc_wake_some_streams(qcc);
|
/* 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_refresh_timeout(qcc);
|
qcc_refresh_timeout(qcc);
|
||||||
|
|
||||||
@ -4149,6 +4177,6 @@ void qcc_show_quic(struct qcc *qcc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct mux_proto_list mux_proto_quic =
|
static struct mux_proto_list mux_proto_quic =
|
||||||
{ .token = IST("quic"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_FE, .mux = &qmux_ops };
|
{ .token = IST("quic"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_BOTH, .mux = &qmux_ops };
|
||||||
|
|
||||||
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_quic);
|
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_quic);
|
||||||
|
@ -286,11 +286,11 @@ int quic_connect_server(struct connection *conn, int flags)
|
|||||||
struct proxy *be;
|
struct proxy *be;
|
||||||
struct conn_src *src;
|
struct conn_src *src;
|
||||||
struct sockaddr_storage *addr;
|
struct sockaddr_storage *addr;
|
||||||
|
struct quic_conn *qc = conn->handle.qc;
|
||||||
|
|
||||||
|
BUG_ON(qc->fd != -1);
|
||||||
BUG_ON(!conn->dst);
|
BUG_ON(!conn->dst);
|
||||||
|
|
||||||
conn->flags |= CO_FL_WAIT_L4_CONN; /* connection in progress */
|
|
||||||
|
|
||||||
switch (obj_type(conn->target)) {
|
switch (obj_type(conn->target)) {
|
||||||
case OBJ_TYPE_PROXY:
|
case OBJ_TYPE_PROXY:
|
||||||
be = __objt_proxy(conn->target);
|
be = __objt_proxy(conn->target);
|
||||||
@ -306,7 +306,7 @@ int quic_connect_server(struct connection *conn, int flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* perform common checks on obtained socket FD, return appropriate Stream Error Flag in case of failure */
|
/* perform common checks on obtained socket FD, return appropriate Stream Error Flag in case of failure */
|
||||||
fd = conn->handle.fd = sock_create_server_socket(conn, be, &stream_err);
|
fd = sock_create_server_socket(conn, be, PROTO_TYPE_DGRAM, SOCK_DGRAM, &stream_err);
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
return stream_err;
|
return stream_err;
|
||||||
|
|
||||||
@ -413,72 +413,25 @@ int quic_connect_server(struct connection *conn, int flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (global.tune.server_sndbuf)
|
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)
|
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;
|
addr = (conn->flags & CO_FL_SOCKS4) ? &srv->socks4_addr : conn->dst;
|
||||||
if (connect(fd, (const struct sockaddr *)addr, get_addr_len(addr)) == -1) {
|
if (connect(fd, (const struct sockaddr *)addr, get_addr_len(addr)) == -1) {
|
||||||
if (errno == EINPROGRESS || errno == EALREADY) {
|
port_range_release_port(fdinfo[fd].port_range, fdinfo[fd].local_port);
|
||||||
/* common case, let's wait for connect status */
|
fdinfo[fd].port_range = NULL;
|
||||||
conn->flags |= CO_FL_WAIT_L4_CONN;
|
close(fd);
|
||||||
}
|
conn->flags |= CO_FL_ERROR;
|
||||||
else if (errno == EISCONN) {
|
return SF_ERR_SRVCL;
|
||||||
/* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
conn_ctrl_init(conn); /* registers the FD */
|
qc->fd = fd;
|
||||||
HA_ATOMIC_OR(&fdtab[fd].state, FD_LINGER_RISK); /* close hard if needed */
|
fd_insert(fd, qc, quic_conn_sock_fd_iocb, tgid, ti->ltid_bit);
|
||||||
|
fd_want_recv(fd);
|
||||||
if (conn->flags & CO_FL_WAIT_L4_CONN) {
|
|
||||||
fd_want_send(fd);
|
|
||||||
fd_cant_send(fd);
|
|
||||||
fd_cant_recv(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
conn_ctrl_init(conn);
|
||||||
return SF_ERR_NONE; /* connection is OK */
|
return SF_ERR_NONE; /* connection is OK */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,7 +397,7 @@ int tcp_connect_server(struct connection *conn, int flags)
|
|||||||
|
|
||||||
|
|
||||||
/* perform common checks on obtained socket FD, return appropriate Stream Error Flag in case of failure */
|
/* perform common checks on obtained socket FD, return appropriate Stream Error Flag in case of failure */
|
||||||
fd = conn->handle.fd = sock_create_server_socket(conn, be, &stream_err);
|
fd = conn->handle.fd = sock_create_server_socket(conn, be, PROTO_TYPE_STREAM, SOCK_STREAM, &stream_err);
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
return stream_err;
|
return stream_err;
|
||||||
|
|
||||||
|
@ -330,7 +330,7 @@ static int uxst_connect_server(struct connection *conn, int flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* perform common checks on obtained socket FD, return appropriate Stream Error Flag in case of failure */
|
/* perform common checks on obtained socket FD, return appropriate Stream Error Flag in case of failure */
|
||||||
fd = conn->handle.fd = sock_create_server_socket(conn, be, &stream_err);
|
fd = conn->handle.fd = sock_create_server_socket(conn, be, PROTO_TYPE_STREAM, SOCK_STREAM, &stream_err);
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
return stream_err;
|
return stream_err;
|
||||||
|
|
||||||
|
@ -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;
|
const struct quic_conn *qc = qcc->conn->handle.qc;
|
||||||
|
|
||||||
chunk_appendf(msg, " qcc=%p(F)", qcc);
|
chunk_appendf(msg, " qcc=%p(%c)", qcc, (qcc->flags & QC_CF_IS_BACK) ? 'B' : 'F');
|
||||||
if (qcc->conn->handle.qc)
|
if (qcc->conn->handle.qc)
|
||||||
chunk_appendf(msg, " qc=%p", 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",
|
chunk_appendf(msg, " .st=%s .sc=%llu .hreq=%llu .flg=0x%04x",
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include <haproxy/cli.h>
|
#include <haproxy/cli.h>
|
||||||
#include <haproxy/list.h>
|
#include <haproxy/list.h>
|
||||||
#include <haproxy/mux_quic.h>
|
#include <haproxy/mux_quic.h>
|
||||||
#include <haproxy/quic_conn-t.h>
|
#include <haproxy/quic_conn.h>
|
||||||
#include <haproxy/quic_tp.h>
|
#include <haproxy/quic_tp.h>
|
||||||
#include <haproxy/quic_utils.h>
|
#include <haproxy/quic_utils.h>
|
||||||
#include <haproxy/tools.h>
|
#include <haproxy/tools.h>
|
||||||
@ -181,9 +181,11 @@ static void dump_quic_oneline(struct show_quic_ctx *ctx, struct quic_conn *qc)
|
|||||||
char bufaddr[INET6_ADDRSTRLEN], bufport[6];
|
char bufaddr[INET6_ADDRSTRLEN], bufport[6];
|
||||||
int ret;
|
int ret;
|
||||||
unsigned char cid_len;
|
unsigned char cid_len;
|
||||||
|
struct listener *l = objt_listener(qc->target);
|
||||||
|
|
||||||
ret = chunk_appendf(&trash, "%p[%02u]/%-.12s ", qc, ctx->thr,
|
ret = chunk_appendf(&trash, "%p[%02u]/%-.12s ", qc, ctx->thr,
|
||||||
qc->li->bind_conf->frontend->id);
|
l ? l->bind_conf->frontend->id : __objt_server(qc->target)->id);
|
||||||
|
|
||||||
chunk_appendf(&trash, "%*s", 36 - ret, " "); /* align output */
|
chunk_appendf(&trash, "%*s", 36 - ret, " "); /* align output */
|
||||||
|
|
||||||
/* State */
|
/* State */
|
||||||
|
206
src/quic_conn.c
206
src/quic_conn.c
@ -119,6 +119,10 @@ const struct quic_version quic_versions[] = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const struct quic_version *quic_version_draft_29 = &quic_versions[0];
|
||||||
|
const struct quic_version *quic_version_1 = &quic_versions[1];
|
||||||
|
const struct quic_version *quic_version_2 = &quic_versions[2];
|
||||||
|
|
||||||
/* Function pointers, can be used to compute a hash from first generated CID and to derive new CIDs */
|
/* Function pointers, can be used to compute a hash from first generated CID and to derive new CIDs */
|
||||||
uint64_t (*quic_hash64_from_cid)(const unsigned char *cid, int size, const unsigned char *secret, size_t secretlen) = NULL;
|
uint64_t (*quic_hash64_from_cid)(const unsigned char *cid, int size, const unsigned char *secret, size_t secretlen) = NULL;
|
||||||
void (*quic_newcid_from_hash64)(unsigned char *cid, int size, uint64_t hash, const unsigned char *secret, size_t secretlen) = NULL;
|
void (*quic_newcid_from_hash64)(unsigned char *cid, int size, uint64_t hash, const unsigned char *secret, size_t secretlen) = NULL;
|
||||||
@ -745,7 +749,7 @@ static struct quic_conn_closed *qc_new_cc_conn(struct quic_conn *qc)
|
|||||||
cc_qc->dcid = qc->dcid;
|
cc_qc->dcid = qc->dcid;
|
||||||
cc_qc->scid = qc->scid;
|
cc_qc->scid = qc->scid;
|
||||||
|
|
||||||
cc_qc->li = qc->li;
|
cc_qc->target = qc->target;
|
||||||
cc_qc->cids = qc->cids;
|
cc_qc->cids = qc->cids;
|
||||||
|
|
||||||
cc_qc->idle_timer_task = qc->idle_timer_task;
|
cc_qc->idle_timer_task = qc->idle_timer_task;
|
||||||
@ -771,6 +775,7 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
|
|||||||
struct list send_list = LIST_HEAD_INIT(send_list);
|
struct list send_list = LIST_HEAD_INIT(send_list);
|
||||||
struct quic_enc_level *qel;
|
struct quic_enc_level *qel;
|
||||||
int st;
|
int st;
|
||||||
|
int discard_hpktns = 0;
|
||||||
struct tasklet *tl = (struct tasklet *)t;
|
struct tasklet *tl = (struct tasklet *)t;
|
||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
|
TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
|
||||||
@ -818,16 +823,30 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
st = qc->state;
|
st = qc->state;
|
||||||
if (st >= QUIC_HS_ST_COMPLETE) {
|
|
||||||
if (!(qc->flags & QUIC_FL_CONN_HPKTNS_DCD)) {
|
|
||||||
/* Discard the Handshake packet number space. */
|
|
||||||
TRACE_PROTO("discarding Handshake pktns", QUIC_EV_CONN_PHPKTS, qc);
|
|
||||||
quic_pktns_discard(qc->hel->pktns, qc);
|
|
||||||
qc_set_timer(qc);
|
|
||||||
qc_el_rx_pkts_del(qc->hel);
|
|
||||||
qc_release_pktns_frms(qc, qc->hel->pktns);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (qc_is_listener(qc)) {
|
||||||
|
if (st >= QUIC_HS_ST_COMPLETE && !quic_tls_pktns_is_dcd(qc, qc->hpktns))
|
||||||
|
discard_hpktns = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (st >= QUIC_HS_ST_CONFIRMED && !quic_tls_pktns_is_dcd(qc, qc->hpktns))
|
||||||
|
discard_hpktns = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (discard_hpktns) {
|
||||||
|
/* Discard the Handshake packet number space. */
|
||||||
|
TRACE_PROTO("discarding Handshake pktns", QUIC_EV_CONN_PHPKTS, qc);
|
||||||
|
quic_pktns_discard(qc->hel->pktns, qc);
|
||||||
|
qc_set_timer(qc);
|
||||||
|
qc_el_rx_pkts_del(qc->hel);
|
||||||
|
qc_release_pktns_frms(qc, qc->hel->pktns);
|
||||||
|
if (!qc_is_listener(qc)) {
|
||||||
|
/* I/O callback switch */
|
||||||
|
qc->wait_event.tasklet->process = quic_conn_app_io_cb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qc_is_listener(qc) && st >= QUIC_HS_ST_COMPLETE) {
|
||||||
/* Note: if no token for address validation was received
|
/* Note: if no token for address validation was received
|
||||||
* for a 0RTT connection, some 0RTT packet could still be
|
* for a 0RTT connection, some 0RTT packet could still be
|
||||||
* waiting for HP removal AFTER the successful handshake completion.
|
* waiting for HP removal AFTER the successful handshake completion.
|
||||||
@ -885,6 +904,26 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* RFC 9001. 4.9.1. Discarding Initial Keys
|
||||||
|
*
|
||||||
|
* The successful use of Handshake packets indicates that no more Initial
|
||||||
|
* packets need to be exchanged, as these keys can only be produced after
|
||||||
|
* receiving all CRYPTO frames from Initial packets. Thus, a client MUST
|
||||||
|
* discard Initial keys when it first sends a Handshake packet...
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!qc_is_listener(qc) && !quic_tls_pktns_is_dcd(qc, qc->ipktns) &&
|
||||||
|
qc->hpktns && qc->hpktns->tx.in_flight > 0) {
|
||||||
|
/* Discard the Initial packet number space. */
|
||||||
|
TRACE_PROTO("discarding Initial pktns", QUIC_EV_CONN_PRSHPKT, qc);
|
||||||
|
quic_pktns_discard(qc->ipktns, qc);
|
||||||
|
qc_set_timer(qc);
|
||||||
|
qc_el_rx_pkts_del(qc->iel);
|
||||||
|
qc_release_pktns_frms(qc, qc->ipktns);
|
||||||
|
}
|
||||||
|
|
||||||
|
qc_txb_release(qc);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
/* Release the Handshake encryption level and packet number space if
|
/* Release the Handshake encryption level and packet number space if
|
||||||
* the Handshake is confirmed and if there is no need to send
|
* the Handshake is confirmed and if there is no need to send
|
||||||
@ -1008,7 +1047,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>
|
/* 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
|
* boolean is set to 1 for IPv4 connection, 0 for IPv6. <server> is set to 1
|
||||||
* for QUIC servers (or haproxy listeners).
|
* for QUIC servers (or haproxy listeners), 0 for QUIC clients.
|
||||||
* <dcid> is the destination connection ID, <scid> is the source connection ID.
|
* <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>
|
* 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
|
* which is the first CID of this connection but a different internal
|
||||||
@ -1021,6 +1060,9 @@ 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.
|
* into the Retry token sent to the client before instantiated this connection.
|
||||||
* Endpoints addresses are specified via <local_addr> and <peer_addr>.
|
* Endpoints addresses are specified via <local_addr> and <peer_addr>.
|
||||||
* Returns the connection if succeeded, NULL if not.
|
* 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_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||||
struct quic_cid *dcid, struct quic_cid *scid,
|
struct quic_cid *dcid, struct quic_cid *scid,
|
||||||
@ -1028,11 +1070,13 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
struct quic_connection_id *conn_id,
|
struct quic_connection_id *conn_id,
|
||||||
struct sockaddr_storage *local_addr,
|
struct sockaddr_storage *local_addr,
|
||||||
struct sockaddr_storage *peer_addr,
|
struct sockaddr_storage *peer_addr,
|
||||||
int server, int token, void *owner)
|
int token, void *target,
|
||||||
|
struct connection *conn)
|
||||||
{
|
{
|
||||||
struct quic_conn *qc = NULL;
|
struct quic_conn *qc = NULL;
|
||||||
struct listener *l = server ? owner : NULL;
|
struct listener *l = objt_listener(target);
|
||||||
struct proxy *prx = l ? l->bind_conf->frontend : NULL;
|
struct server *srv = objt_server(target);
|
||||||
|
struct proxy *prx = l ? l->bind_conf->frontend : __objt_server(target)->proxy;
|
||||||
struct quic_cc_algo *cc_algo = NULL;
|
struct quic_cc_algo *cc_algo = NULL;
|
||||||
unsigned int next_actconn = 0, next_sslconn = 0, next_handshake = 0;
|
unsigned int next_actconn = 0, next_sslconn = 0, next_handshake = 0;
|
||||||
|
|
||||||
@ -1051,7 +1095,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server) {
|
if (l) {
|
||||||
next_handshake = quic_increment_curr_handshake(l);
|
next_handshake = quic_increment_curr_handshake(l);
|
||||||
if (!next_handshake) {
|
if (!next_handshake) {
|
||||||
TRACE_STATE("max handshake reached", QUIC_EV_CONN_INIT);
|
TRACE_STATE("max handshake reached", QUIC_EV_CONN_INIT);
|
||||||
@ -1065,6 +1109,14 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qc->cids = pool_alloc(pool_head_quic_cids);
|
||||||
|
if (!qc->cids) {
|
||||||
|
TRACE_ERROR("Could not allocate a new CID tree", QUIC_EV_CONN_INIT, qc);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
qc->target = target;
|
||||||
|
*qc->cids = EB_ROOT;
|
||||||
/* Now that quic_conn instance is allocated, quic_conn_release() will
|
/* Now that quic_conn instance is allocated, quic_conn_release() will
|
||||||
* ensure global accounting is decremented.
|
* ensure global accounting is decremented.
|
||||||
*/
|
*/
|
||||||
@ -1082,7 +1134,6 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
qc->streams_by_id = EB_ROOT_UNIQUE;
|
qc->streams_by_id = EB_ROOT_UNIQUE;
|
||||||
|
|
||||||
/* Required to call free_quic_conn_cids() from quic_conn_release() */
|
/* Required to call free_quic_conn_cids() from quic_conn_release() */
|
||||||
qc->cids = NULL;
|
|
||||||
qc->tx.cc_buf_area = NULL;
|
qc->tx.cc_buf_area = NULL;
|
||||||
qc_init_fd(qc);
|
qc_init_fd(qc);
|
||||||
|
|
||||||
@ -1096,7 +1147,7 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
qc->idle_timer_task = NULL;
|
qc->idle_timer_task = NULL;
|
||||||
|
|
||||||
qc->xprt_ctx = NULL;
|
qc->xprt_ctx = NULL;
|
||||||
qc->conn = NULL;
|
qc->conn = conn;
|
||||||
qc->qcc = NULL;
|
qc->qcc = NULL;
|
||||||
qc->app_ops = NULL;
|
qc->app_ops = NULL;
|
||||||
qc->path = NULL;
|
qc->path = NULL;
|
||||||
@ -1117,16 +1168,12 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
/* Packet number spaces */
|
/* Packet number spaces */
|
||||||
qc->ipktns = qc->hpktns = qc->apktns = NULL;
|
qc->ipktns = qc->hpktns = qc->apktns = NULL;
|
||||||
LIST_INIT(&qc->pktns_list);
|
LIST_INIT(&qc->pktns_list);
|
||||||
|
qc->prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module);
|
||||||
/* Required to safely call quic_conn_prx_cntrs_update() from quic_conn_release(). */
|
|
||||||
qc->prx_counters = NULL;
|
|
||||||
|
|
||||||
/* QUIC Server (or listener). */
|
/* QUIC Server (or listener). */
|
||||||
if (server) {
|
if (l) {
|
||||||
cc_algo = l->bind_conf->quic_cc_algo;
|
cc_algo = l->bind_conf->quic_cc_algo;
|
||||||
|
|
||||||
qc->prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe,
|
|
||||||
&quic_stats_module);
|
|
||||||
qc->flags = QUIC_FL_CONN_LISTENER;
|
qc->flags = QUIC_FL_CONN_LISTENER;
|
||||||
/* Mark this connection as having not received any token when 0-RTT is enabled. */
|
/* Mark this connection as having not received any token when 0-RTT is enabled. */
|
||||||
if (l->bind_conf->ssl_conf.early_data && !token)
|
if (l->bind_conf->ssl_conf.early_data && !token)
|
||||||
@ -1137,29 +1184,52 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
/* Copy the packet SCID to reuse it as DCID for sending */
|
/* Copy the packet SCID to reuse it as DCID for sending */
|
||||||
qc->dcid = *scid;
|
qc->dcid = *scid;
|
||||||
qc->tx.buf = BUF_NULL;
|
qc->tx.buf = BUF_NULL;
|
||||||
qc->li = l;
|
conn_id->qc = qc;
|
||||||
}
|
}
|
||||||
/* QUIC Client (outgoing connection to servers) */
|
/* QUIC Client (outgoing connection to servers) */
|
||||||
else {
|
else {
|
||||||
|
struct quic_connection_id *conn_cid = NULL;
|
||||||
|
|
||||||
|
qc->flags = QUIC_FL_CONN_PEER_VALIDATED_ADDR;
|
||||||
qc->state = QUIC_HS_ST_CLIENT_INITIAL;
|
qc->state = QUIC_HS_ST_CLIENT_INITIAL;
|
||||||
if (dcid->len)
|
|
||||||
memcpy(qc->dcid.data, dcid->data, dcid->len);
|
memset(&qc->odcid, 0, sizeof qc->odcid);
|
||||||
qc->dcid.len = dcid->len;
|
qc->odcid.len = 0;
|
||||||
qc->li = NULL;
|
/* This is the original connection ID from the peer server
|
||||||
|
* point of view.
|
||||||
|
*/
|
||||||
|
if (RAND_bytes(qc->dcid.data, sizeof(qc->dcid.data)) != 1)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
qc->dcid.len = sizeof(qc->dcid.data);
|
||||||
|
|
||||||
|
conn_cid = new_quic_cid(qc->cids, qc, NULL, NULL);
|
||||||
|
if (!conn_cid)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
_quic_cid_insert(conn_cid);
|
||||||
|
dcid = &qc->dcid;
|
||||||
|
conn_id = conn_cid;
|
||||||
|
|
||||||
|
qc->tx.buf = BUF_NULL;
|
||||||
|
qc->next_cid_seq_num = 1;
|
||||||
|
conn->handle.qc = qc;
|
||||||
}
|
}
|
||||||
qc->mux_state = QC_MUX_NULL;
|
qc->mux_state = QC_MUX_NULL;
|
||||||
qc->err = quic_err_transport(QC_ERR_NO_ERROR);
|
qc->err = quic_err_transport(QC_ERR_NO_ERROR);
|
||||||
|
|
||||||
/* If connection is instantiated due to an INITIAL packet with an
|
/* Listener only: if connection is instantiated due to an INITIAL packet with an
|
||||||
* already checked token, consider the peer address as validated.
|
* already checked token, consider the peer address as validated.
|
||||||
*/
|
*/
|
||||||
if (token) {
|
if (l) {
|
||||||
TRACE_STATE("validate peer address due to initial token",
|
if (token_odcid->len) {
|
||||||
QUIC_EV_CONN_INIT, qc);
|
TRACE_STATE("validate peer address due to initial token",
|
||||||
qc->flags |= QUIC_FL_CONN_PEER_VALIDATED_ADDR;
|
QUIC_EV_CONN_INIT, qc);
|
||||||
}
|
qc->flags |= QUIC_FL_CONN_PEER_VALIDATED_ADDR;
|
||||||
else {
|
}
|
||||||
HA_ATOMIC_INC(&qc->prx_counters->half_open_conn);
|
else {
|
||||||
|
HA_ATOMIC_INC(&qc->prx_counters->half_open_conn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now proceeds to allocation of qc members. */
|
/* Now proceeds to allocation of qc members. */
|
||||||
@ -1169,16 +1239,8 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
qc->cids = pool_alloc(pool_head_quic_cids);
|
/* Listener only */
|
||||||
if (!qc->cids) {
|
if (l && HA_ATOMIC_LOAD(&l->rx.quic_mode) == QUIC_SOCK_MODE_CONN &&
|
||||||
TRACE_ERROR("Could not allocate a new CID tree", QUIC_EV_CONN_INIT, qc);
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
*qc->cids = EB_ROOT;
|
|
||||||
|
|
||||||
conn_id->qc = qc;
|
|
||||||
|
|
||||||
if (HA_ATOMIC_LOAD(&l->rx.quic_mode) == QUIC_SOCK_MODE_CONN &&
|
|
||||||
(quic_tune.options & QUIC_TUNE_SOCK_PER_CONN) &&
|
(quic_tune.options & QUIC_TUNE_SOCK_PER_CONN) &&
|
||||||
is_addr(local_addr)) {
|
is_addr(local_addr)) {
|
||||||
TRACE_USER("Allocate a socket for QUIC connection", QUIC_EV_CONN_INIT, qc);
|
TRACE_USER("Allocate a socket for QUIC connection", QUIC_EV_CONN_INIT, qc);
|
||||||
@ -1217,6 +1279,10 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
qc->rx.buf = b_make(qc->rx.buf.area, QUIC_CONN_RX_BUFSZ, 0, 0);
|
qc->rx.buf = b_make(qc->rx.buf.area, QUIC_CONN_RX_BUFSZ, 0, 0);
|
||||||
qc->rx.stream_max_uni = qc->rx.stream_max_bidi = 0;
|
qc->rx.stream_max_uni = qc->rx.stream_max_bidi = 0;
|
||||||
|
|
||||||
|
qc->max_udp_payload = l ?
|
||||||
|
l->bind_conf->quic_params.max_udp_payload_size :
|
||||||
|
srv->quic_params.max_udp_payload_size;
|
||||||
|
|
||||||
qc->nb_pkt_for_cc = 1;
|
qc->nb_pkt_for_cc = 1;
|
||||||
qc->nb_pkt_since_cc = 0;
|
qc->nb_pkt_since_cc = 0;
|
||||||
|
|
||||||
@ -1228,17 +1294,24 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
qc->max_ack_delay = 0;
|
qc->max_ack_delay = 0;
|
||||||
/* Only one path at this time (multipath not supported) */
|
/* Only one path at this time (multipath not supported) */
|
||||||
qc->path = &qc->paths[0];
|
qc->path = &qc->paths[0];
|
||||||
quic_cc_path_init(qc->path, ipv4, server ? l->bind_conf->max_cwnd : 0,
|
quic_cc_path_init(qc->path, ipv4, l ? l->bind_conf->max_cwnd : 0,
|
||||||
cc_algo ? cc_algo : default_quic_cc_algo, qc);
|
cc_algo ? cc_algo : default_quic_cc_algo, qc);
|
||||||
|
|
||||||
memcpy(&qc->local_addr, local_addr, sizeof(qc->local_addr));
|
if (local_addr)
|
||||||
|
memcpy(&qc->local_addr, local_addr, sizeof(qc->local_addr));
|
||||||
|
else
|
||||||
|
memset(&qc->local_addr, 0, sizeof(qc->local_addr));
|
||||||
memcpy(&qc->peer_addr, peer_addr, sizeof qc->peer_addr);
|
memcpy(&qc->peer_addr, peer_addr, sizeof qc->peer_addr);
|
||||||
|
|
||||||
if (server && !qc_lstnr_params_init(qc, &l->bind_conf->quic_params,
|
if (l) {
|
||||||
conn_id->stateless_reset_token,
|
qc_lstnr_params_init(qc, &l->bind_conf->quic_params,
|
||||||
dcid->data, dcid->len,
|
conn_id->stateless_reset_token,
|
||||||
qc->scid.data, qc->scid.len, token_odcid))
|
qc->dcid.data, qc->dcid.len,
|
||||||
goto err;
|
qc->scid.data, qc->scid.len, token_odcid);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qc_srv_params_init(qc, &srv->quic_params, qc->scid.data, qc->scid.len);
|
||||||
|
}
|
||||||
|
|
||||||
/* Initialize the idle timeout of the connection at the "max_idle_timeout"
|
/* Initialize the idle timeout of the connection at the "max_idle_timeout"
|
||||||
* value from local transport parameters.
|
* value from local transport parameters.
|
||||||
@ -1259,12 +1332,12 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
qc->wait_event.events = 0;
|
qc->wait_event.events = 0;
|
||||||
qc->subs = NULL;
|
qc->subs = NULL;
|
||||||
|
|
||||||
if (qc_alloc_ssl_sock_ctx(qc) ||
|
if (qc_alloc_ssl_sock_ctx(qc, conn) ||
|
||||||
!quic_conn_init_timer(qc) ||
|
!quic_conn_init_timer(qc) ||
|
||||||
!quic_conn_init_idle_timer_task(qc, prx))
|
!quic_conn_init_idle_timer_task(qc, prx))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if (!qc_new_isecs(qc, &qc->iel->tls_ctx, qc->original_version, dcid->data, dcid->len, 1))
|
if (!qc_new_isecs(qc, &qc->iel->tls_ctx, qc->original_version, dcid->data, dcid->len, !!l))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/* Counters initialization */
|
/* Counters initialization */
|
||||||
@ -1278,6 +1351,15 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
|||||||
return qc;
|
return qc;
|
||||||
|
|
||||||
err:
|
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);
|
quic_conn_release(qc);
|
||||||
|
|
||||||
/* Decrement global counters. Done only for errors happening before or
|
/* Decrement global counters. Done only for errors happening before or
|
||||||
@ -1314,7 +1396,7 @@ int qc_handle_conn_migration(struct quic_conn *qc,
|
|||||||
* used during the handshake, unless the endpoint has acted on a
|
* used during the handshake, unless the endpoint has acted on a
|
||||||
* preferred_address transport parameter from the peer.
|
* preferred_address transport parameter from the peer.
|
||||||
*/
|
*/
|
||||||
if (qc->li->bind_conf->quic_params.disable_active_migration) {
|
if (__objt_listener(qc->target)->bind_conf->quic_params.disable_active_migration) {
|
||||||
TRACE_ERROR("Active migration was disabled, datagram dropped", QUIC_EV_CONN_LPKT, qc);
|
TRACE_ERROR("Active migration was disabled, datagram dropped", QUIC_EV_CONN_LPKT, qc);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
@ -1444,8 +1526,8 @@ int quic_conn_release(struct quic_conn *qc)
|
|||||||
*/
|
*/
|
||||||
if (MT_LIST_INLIST(&qc->accept_list)) {
|
if (MT_LIST_INLIST(&qc->accept_list)) {
|
||||||
MT_LIST_DELETE(&qc->accept_list);
|
MT_LIST_DELETE(&qc->accept_list);
|
||||||
BUG_ON(qc->li->rx.quic_curr_accept == 0);
|
BUG_ON(__objt_listener(qc->target)->rx.quic_curr_accept == 0);
|
||||||
HA_ATOMIC_DEC(&qc->li->rx.quic_curr_accept);
|
HA_ATOMIC_DEC(&__objt_listener(qc->target)->rx.quic_curr_accept);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Substract last congestion window from global memory counter. */
|
/* Substract last congestion window from global memory counter. */
|
||||||
@ -1515,8 +1597,8 @@ int quic_conn_release(struct quic_conn *qc)
|
|||||||
/* Connection released before handshake completion. */
|
/* Connection released before handshake completion. */
|
||||||
if (unlikely(qc->state < QUIC_HS_ST_COMPLETE)) {
|
if (unlikely(qc->state < QUIC_HS_ST_COMPLETE)) {
|
||||||
if (qc_is_listener(qc)) {
|
if (qc_is_listener(qc)) {
|
||||||
BUG_ON(qc->li->rx.quic_curr_handshake == 0);
|
BUG_ON(__objt_listener(qc->target)->rx.quic_curr_handshake == 0);
|
||||||
HA_ATOMIC_DEC(&qc->li->rx.quic_curr_handshake);
|
HA_ATOMIC_DEC(&__objt_listener(qc->target)->rx.quic_curr_handshake);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1924,8 +2006,8 @@ void qc_bind_tid_commit(struct quic_conn *qc, struct listener *new_li)
|
|||||||
/* At this point no connection was accounted for yet on this
|
/* At this point no connection was accounted for yet on this
|
||||||
* listener so it's OK to just swap the pointer.
|
* listener so it's OK to just swap the pointer.
|
||||||
*/
|
*/
|
||||||
if (new_li && new_li != qc->li)
|
if (new_li && new_li != __objt_listener(qc->target))
|
||||||
qc->li = new_li;
|
qc->target = &new_li->obj_type;
|
||||||
|
|
||||||
/* Rebind the connection FD. */
|
/* Rebind the connection FD. */
|
||||||
if (qc_test_fd(qc)) {
|
if (qc_test_fd(qc)) {
|
||||||
|
@ -500,11 +500,19 @@ static int quic_parse_new_token_frame(struct quic_frame *frm, struct quic_conn *
|
|||||||
{
|
{
|
||||||
struct qf_new_token *new_token_frm = &frm->new_token;
|
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;
|
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);
|
memcpy(new_token_frm->data, *pos, new_token_frm->len);
|
||||||
|
#endif
|
||||||
|
|
||||||
*pos += new_token_frm->len;
|
*pos += new_token_frm->len;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -58,7 +58,7 @@ static int qc_ssl_compat_add_tps_cb(SSL *ssl, unsigned int ext_type, unsigned in
|
|||||||
int quic_tls_compat_init(struct bind_conf *bind_conf, SSL_CTX *ctx)
|
int quic_tls_compat_init(struct bind_conf *bind_conf, SSL_CTX *ctx)
|
||||||
{
|
{
|
||||||
/* Ignore non-QUIC connections */
|
/* Ignore non-QUIC connections */
|
||||||
if (bind_conf->xprt != xprt_get(XPRT_QUIC))
|
if (bind_conf && bind_conf->xprt != xprt_get(XPRT_QUIC))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* This callback is already registered if the TLS keylog is activated for
|
/* This callback is already registered if the TLS keylog is activated for
|
||||||
@ -150,22 +150,22 @@ void quic_tls_compat_keylog_callback(const SSL *ssl, const char *line)
|
|||||||
if (sizeof(QUIC_OPENSSL_COMPAT_CLIENT_HANDSHAKE) - 1 == n &&
|
if (sizeof(QUIC_OPENSSL_COMPAT_CLIENT_HANDSHAKE) - 1 == n &&
|
||||||
!strncmp(start, QUIC_OPENSSL_COMPAT_CLIENT_HANDSHAKE, n)) {
|
!strncmp(start, QUIC_OPENSSL_COMPAT_CLIENT_HANDSHAKE, n)) {
|
||||||
level = ssl_encryption_handshake;
|
level = ssl_encryption_handshake;
|
||||||
write = 0;
|
write = qc_is_listener(qc) ? 0 : 1;
|
||||||
}
|
}
|
||||||
else if (sizeof(QUIC_OPENSSL_COMPAT_SERVER_HANDSHAKE) - 1 == n &&
|
else if (sizeof(QUIC_OPENSSL_COMPAT_SERVER_HANDSHAKE) - 1 == n &&
|
||||||
!strncmp(start, QUIC_OPENSSL_COMPAT_SERVER_HANDSHAKE, n)) {
|
!strncmp(start, QUIC_OPENSSL_COMPAT_SERVER_HANDSHAKE, n)) {
|
||||||
level = ssl_encryption_handshake;
|
level = ssl_encryption_handshake;
|
||||||
write = 1;
|
write = qc_is_listener(qc) ? 1 : 0;
|
||||||
}
|
}
|
||||||
else if (sizeof(QUIC_OPENSSL_COMPAT_CLIENT_APPLICATION) - 1 == n &&
|
else if (sizeof(QUIC_OPENSSL_COMPAT_CLIENT_APPLICATION) - 1 == n &&
|
||||||
!strncmp(start, QUIC_OPENSSL_COMPAT_CLIENT_APPLICATION, n)) {
|
!strncmp(start, QUIC_OPENSSL_COMPAT_CLIENT_APPLICATION, n)) {
|
||||||
level = ssl_encryption_application;
|
level = ssl_encryption_application;
|
||||||
write = 0;
|
write = qc_is_listener(qc) ? 0 : 1;
|
||||||
}
|
}
|
||||||
else if (sizeof(QUIC_OPENSSL_COMPAT_SERVER_APPLICATION) - 1 == n &&
|
else if (sizeof(QUIC_OPENSSL_COMPAT_SERVER_APPLICATION) - 1 == n &&
|
||||||
!strncmp(start, QUIC_OPENSSL_COMPAT_SERVER_APPLICATION, n)) {
|
!strncmp(start, QUIC_OPENSSL_COMPAT_SERVER_APPLICATION, n)) {
|
||||||
level = ssl_encryption_application;
|
level = ssl_encryption_application;
|
||||||
write = 1;
|
write = qc_is_listener(qc) ? 1 : 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
goto leave;
|
goto leave;
|
||||||
|
100
src/quic_rx.c
100
src/quic_rx.c
@ -949,7 +949,11 @@ static int qc_parse_pkt_frms(struct quic_conn *qc, struct quic_rx_packet *pkt,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* TODO */
|
/* 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.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case QUIC_FT_STREAM_8 ... QUIC_FT_STREAM_F:
|
case QUIC_FT_STREAM_8 ... QUIC_FT_STREAM_F:
|
||||||
@ -1819,8 +1823,8 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt,
|
|||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
qc = qc_new_conn(pkt->version, ipv4, &pkt->dcid, &pkt->scid, &token_odcid,
|
qc = qc_new_conn(pkt->version, ipv4, &pkt->dcid, &pkt->scid, &token_odcid,
|
||||||
conn_id, &dgram->daddr, &pkt->saddr, 1,
|
conn_id, &dgram->daddr, &pkt->saddr,
|
||||||
!!pkt->token_len, l);
|
!!pkt->token_len, l, NULL);
|
||||||
if (qc == NULL) {
|
if (qc == NULL) {
|
||||||
pool_free(pool_head_quic_connection_id, conn_id);
|
pool_free(pool_head_quic_connection_id, conn_id);
|
||||||
goto err;
|
goto err;
|
||||||
@ -1877,25 +1881,25 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt,
|
|||||||
/* Parse a QUIC packet starting at <pos>. Data won't be read after <end> even
|
/* Parse a QUIC packet starting at <pos>. Data won't be read after <end> even
|
||||||
* if the packet is incomplete. This function will populate fields of <pkt>
|
* if the packet is incomplete. This function will populate fields of <pkt>
|
||||||
* instance, most notably its length. <dgram> is the UDP datagram which
|
* instance, most notably its length. <dgram> is the UDP datagram which
|
||||||
* contains the parsed packet. <l> is the listener instance on which it was
|
* contains the parsed packet. <o> is the address object type address of the
|
||||||
* received.
|
* object which receives this received packet. <qc> is the QUIC connection,
|
||||||
|
* only valid for QUIC clients.
|
||||||
*
|
*
|
||||||
* Returns 0 on success else non-zero. Packet length is guaranteed to be set to
|
* Returns 0 on success else non-zero. Packet length is guaranteed to be set to
|
||||||
* the real packet value or to cover all data between <pos> and <end> : this is
|
* the real packet value or to cover all data between <pos> and <end> : this is
|
||||||
* useful to reject a whole datagram.
|
* useful to reject a whole datagram.
|
||||||
*/
|
*/
|
||||||
static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
|
static int quic_rx_pkt_parse(struct quic_conn *qc, struct quic_rx_packet *pkt,
|
||||||
unsigned char *pos, const unsigned char *end,
|
unsigned char *pos, const unsigned char *end,
|
||||||
struct quic_dgram *dgram, struct listener *l)
|
struct quic_dgram *dgram, enum obj_type *o)
|
||||||
{
|
{
|
||||||
const unsigned char *beg = pos;
|
const unsigned char *beg = pos;
|
||||||
struct proxy *prx;
|
|
||||||
struct quic_counters *prx_counters;
|
struct quic_counters *prx_counters;
|
||||||
|
struct listener *l = objt_listener(o);
|
||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_LPKT);
|
TRACE_ENTER(QUIC_EV_CONN_LPKT);
|
||||||
|
|
||||||
prx = l->bind_conf->frontend;
|
prx_counters = qc_counters(o, &quic_stats_module);
|
||||||
prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module);
|
|
||||||
|
|
||||||
if (end <= pos) {
|
if (end <= pos) {
|
||||||
TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT);
|
TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT);
|
||||||
@ -1951,8 +1955,10 @@ static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
|
|||||||
goto drop;
|
goto drop;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RFC9000 6. Version Negotiation */
|
/* RFC9000 6. Version Negotiation. A Version Negotiation packet is
|
||||||
if (!pkt->version) {
|
* sent only by servers.
|
||||||
|
*/
|
||||||
|
if (l && !pkt->version) {
|
||||||
/* unsupported version, send Negotiation packet */
|
/* unsupported version, send Negotiation packet */
|
||||||
if (send_version_negotiation(l->rx.fd, &dgram->saddr, pkt)) {
|
if (send_version_negotiation(l->rx.fd, &dgram->saddr, pkt)) {
|
||||||
TRACE_ERROR("VN packet not sent", QUIC_EV_CONN_LPKT);
|
TRACE_ERROR("VN packet not sent", QUIC_EV_CONN_LPKT);
|
||||||
@ -1976,6 +1982,13 @@ static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
|
|||||||
goto drop;
|
goto drop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!l && pkt->token_len) {
|
||||||
|
/* A server must sent Initial packets with a null token length. */
|
||||||
|
TRACE_PROTO("Packet dropped",
|
||||||
|
QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version);
|
||||||
|
goto drop;
|
||||||
|
}
|
||||||
|
|
||||||
pkt->token = pos;
|
pkt->token = pos;
|
||||||
pkt->token_len = token_len;
|
pkt->token_len = token_len;
|
||||||
pos += pkt->token_len;
|
pos += pkt->token_len;
|
||||||
@ -2001,26 +2014,49 @@ static int quic_rx_pkt_parse(struct quic_rx_packet *pkt,
|
|||||||
pkt->pn_offset = pos - beg;
|
pkt->pn_offset = pos - beg;
|
||||||
pkt->len = pkt->pn_offset + len;
|
pkt->len = pkt->pn_offset + len;
|
||||||
|
|
||||||
/* 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
|
/* Interrupt parsing after packet length retrieval : this
|
||||||
* ensures that only the packet is dropped but not the whole
|
* ensures that only the packet is dropped but not the whole
|
||||||
* datagram.
|
* datagram.
|
||||||
*/
|
*/
|
||||||
if (pkt->type == QUIC_PACKET_TYPE_0RTT && !l->bind_conf->ssl_conf.early_data) {
|
|
||||||
TRACE_PROTO("RX 0-RTT packet not supported", QUIC_EV_CONN_LPKT);
|
if (pkt->type == QUIC_PACKET_TYPE_INITIAL) {
|
||||||
goto drop;
|
if (l) {
|
||||||
|
/* RFC 9000. Initial Datagram Size
|
||||||
|
*
|
||||||
|
* A server MUST discard an Initial packet that is carried in a UDP datagram
|
||||||
|
* with a payload that is smaller than the smallest allowed maximum datagram
|
||||||
|
* size of 1200 bytes.
|
||||||
|
*/
|
||||||
|
if (dgram->len < QUIC_INITIAL_PACKET_MINLEN) {
|
||||||
|
TRACE_PROTO("RX too short datagram with an Initial packet", QUIC_EV_CONN_LPKT);
|
||||||
|
HA_ATOMIC_INC(&prx_counters->too_short_initial_dgram);
|
||||||
|
goto drop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* TODO: This is not clear if a too short RX datagram which carries ack-eliciting
|
||||||
|
* packets must be dropped by a client. If this is the case, this is not from
|
||||||
|
* here, but after having parsed the datagram frames.
|
||||||
|
*
|
||||||
|
* RFC 9000. Initial Datagram Size
|
||||||
|
*
|
||||||
|
* Similarly, a server MUST expand the payload of all UDP datagrams carrying
|
||||||
|
* ack-eliciting Initial packets to at least the smallest allowed maximum
|
||||||
|
* datagram size of 1200 bytes.
|
||||||
|
*/
|
||||||
|
if (!(qc->flags & QUIC_FL_CONN_SCID_RECEIVED)) {
|
||||||
|
qc->flags |= QUIC_FL_CONN_SCID_RECEIVED;
|
||||||
|
memcpy(qc->dcid.data, pkt->scid.data, pkt->scid.len);
|
||||||
|
qc->dcid.len = pkt->scid.len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pkt->type == QUIC_PACKET_TYPE_0RTT) {
|
||||||
|
/* O-RTT packet are not sent by servers. */
|
||||||
|
if (!l || !l->bind_conf->ssl_conf.early_data) {
|
||||||
|
TRACE_PROTO("RX 0-RTT packet not supported", QUIC_EV_CONN_LPKT);
|
||||||
|
goto drop;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -2242,7 +2278,8 @@ static void qc_rx_pkt_handle(struct quic_conn *qc, struct quic_rx_packet *pkt,
|
|||||||
* this function.
|
* this function.
|
||||||
*
|
*
|
||||||
* If datagram has been received on a quic-conn owned FD, <from_qc> must be set
|
* If datagram has been received on a quic-conn owned FD, <from_qc> must be set
|
||||||
* to the connection instance. <li> is the attached listener. The caller is
|
* to the connection instance. <o> is the object type address of the object
|
||||||
|
* (listener or server) receiving the datagram. The caller is
|
||||||
* responsible to ensure that the first packet is destined to this connection
|
* responsible to ensure that the first packet is destined to this connection
|
||||||
* by comparing CIDs.
|
* by comparing CIDs.
|
||||||
*
|
*
|
||||||
@ -2254,7 +2291,7 @@ static void qc_rx_pkt_handle(struct quic_conn *qc, struct quic_rx_packet *pkt,
|
|||||||
* the datagram may not have been parsed.
|
* the datagram may not have been parsed.
|
||||||
*/
|
*/
|
||||||
int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
|
int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
|
||||||
struct listener *li)
|
enum obj_type *o)
|
||||||
{
|
{
|
||||||
struct quic_rx_packet *pkt;
|
struct quic_rx_packet *pkt;
|
||||||
struct quic_conn *qc = NULL;
|
struct quic_conn *qc = NULL;
|
||||||
@ -2292,7 +2329,7 @@ int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
|
|||||||
pkt->flags |= QUIC_FL_RX_PACKET_DGRAM_FIRST;
|
pkt->flags |= QUIC_FL_RX_PACKET_DGRAM_FIRST;
|
||||||
|
|
||||||
quic_rx_packet_refinc(pkt);
|
quic_rx_packet_refinc(pkt);
|
||||||
if (quic_rx_pkt_parse(pkt, pos, end, dgram, li))
|
if (quic_rx_pkt_parse(from_qc, pkt, pos, end, dgram, o))
|
||||||
goto next;
|
goto next;
|
||||||
|
|
||||||
/* Search quic-conn instance for first packet of the datagram.
|
/* Search quic-conn instance for first packet of the datagram.
|
||||||
@ -2301,6 +2338,7 @@ int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
|
|||||||
*/
|
*/
|
||||||
if (!qc) {
|
if (!qc) {
|
||||||
int new_tid = -1;
|
int new_tid = -1;
|
||||||
|
struct listener *li = objt_listener(o);
|
||||||
|
|
||||||
qc = from_qc ? from_qc : quic_rx_pkt_retrieve_conn(pkt, dgram, li, &new_tid);
|
qc = from_qc ? from_qc : quic_rx_pkt_retrieve_conn(pkt, dgram, li, &new_tid);
|
||||||
/* qc is NULL if receiving a non Initial packet for an
|
/* qc is NULL if receiving a non Initial packet for an
|
||||||
|
@ -87,13 +87,13 @@ int quic_sock_get_dst(struct connection *conn, struct sockaddr *addr, socklen_t
|
|||||||
memcpy(addr, &qc->peer_addr, len);
|
memcpy(addr, &qc->peer_addr, len);
|
||||||
} else {
|
} else {
|
||||||
struct sockaddr_storage *from;
|
struct sockaddr_storage *from;
|
||||||
|
struct listener *l = objt_listener(qc->target);
|
||||||
|
|
||||||
/* Return listener address if IP_PKTINFO or friends are not
|
/* Return listener address if IP_PKTINFO or friends are not
|
||||||
* supported by the socket.
|
* supported by the socket.
|
||||||
*/
|
*/
|
||||||
BUG_ON(!qc->li);
|
BUG_ON(!l);
|
||||||
from = is_addr(&qc->local_addr) ? &qc->local_addr :
|
from = is_addr(&qc->local_addr) ? &qc->local_addr : &l->rx.addr;
|
||||||
&qc->li->rx.addr;
|
|
||||||
if (len > sizeof(*from))
|
if (len > sizeof(*from))
|
||||||
len = sizeof(*from);
|
len = sizeof(*from);
|
||||||
memcpy(addr, from, len);
|
memcpy(addr, from, len);
|
||||||
@ -815,36 +815,30 @@ int qc_snd_buf(struct quic_conn *qc, const struct buffer *buf, size_t sz,
|
|||||||
int qc_rcv_buf(struct quic_conn *qc)
|
int qc_rcv_buf(struct quic_conn *qc)
|
||||||
{
|
{
|
||||||
struct sockaddr_storage saddr = {0}, daddr = {0};
|
struct sockaddr_storage saddr = {0}, daddr = {0};
|
||||||
struct quic_transport_params *params;
|
|
||||||
struct quic_dgram *new_dgram = NULL;
|
struct quic_dgram *new_dgram = NULL;
|
||||||
struct buffer buf = BUF_NULL;
|
struct buffer buf = BUF_NULL;
|
||||||
size_t max_sz;
|
|
||||||
unsigned char *dgram_buf;
|
unsigned char *dgram_buf;
|
||||||
struct listener *l;
|
|
||||||
ssize_t ret = 0;
|
ssize_t ret = 0;
|
||||||
|
struct listener *l = objt_listener(qc->target);
|
||||||
|
|
||||||
/* Do not call this if quic-conn FD is uninitialized. */
|
/* Do not call this if quic-conn FD is uninitialized. */
|
||||||
BUG_ON(qc->fd < 0);
|
BUG_ON(qc->fd < 0);
|
||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_RCV, qc);
|
TRACE_ENTER(QUIC_EV_CONN_RCV, qc);
|
||||||
l = qc->li;
|
|
||||||
|
|
||||||
params = &l->bind_conf->quic_params;
|
|
||||||
max_sz = params->max_udp_payload_size;
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (!b_alloc(&buf, DB_MUX_RX))
|
if (!b_alloc(&buf, DB_MUX_RX))
|
||||||
break; /* TODO subscribe for memory again available. */
|
break; /* TODO subscribe for memory again available. */
|
||||||
|
|
||||||
b_reset(&buf);
|
b_reset(&buf);
|
||||||
BUG_ON(b_contig_space(&buf) < max_sz);
|
BUG_ON(b_contig_space(&buf) < qc->max_udp_payload);
|
||||||
|
|
||||||
/* Allocate datagram on first loop or after requeuing. */
|
/* Allocate datagram on first loop or after requeuing. */
|
||||||
if (!new_dgram && !(new_dgram = pool_alloc(pool_head_quic_dgram)))
|
if (!new_dgram && !(new_dgram = pool_alloc(pool_head_quic_dgram)))
|
||||||
break; /* TODO subscribe for memory again available. */
|
break; /* TODO subscribe for memory again available. */
|
||||||
|
|
||||||
dgram_buf = (unsigned char *)b_tail(&buf);
|
dgram_buf = (unsigned char *)b_tail(&buf);
|
||||||
ret = quic_recv(qc->fd, dgram_buf, max_sz,
|
ret = quic_recv(qc->fd, dgram_buf, qc->max_udp_payload,
|
||||||
(struct sockaddr *)&saddr, sizeof(saddr),
|
(struct sockaddr *)&saddr, sizeof(saddr),
|
||||||
(struct sockaddr *)&daddr, sizeof(daddr),
|
(struct sockaddr *)&daddr, sizeof(daddr),
|
||||||
get_net_port(&qc->local_addr));
|
get_net_port(&qc->local_addr));
|
||||||
@ -876,7 +870,7 @@ int qc_rcv_buf(struct quic_conn *qc)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!qc_check_dcid(qc, new_dgram->dcid, new_dgram->dcid_len)) {
|
if (l && !qc_check_dcid(qc, new_dgram->dcid, new_dgram->dcid_len)) {
|
||||||
/* Datagram received by error on the connection FD, dispatch it
|
/* Datagram received by error on the connection FD, dispatch it
|
||||||
* to its associated quic-conn.
|
* to its associated quic-conn.
|
||||||
*
|
*
|
||||||
@ -934,7 +928,7 @@ int qc_rcv_buf(struct quic_conn *qc)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
quic_dgram_parse(new_dgram, qc, qc->li);
|
quic_dgram_parse(new_dgram, qc, qc->target);
|
||||||
/* A datagram must always be consumed after quic_parse_dgram(). */
|
/* A datagram must always be consumed after quic_parse_dgram(). */
|
||||||
BUG_ON(new_dgram->buf);
|
BUG_ON(new_dgram->buf);
|
||||||
} while (ret > 0);
|
} while (ret > 0);
|
||||||
@ -956,11 +950,13 @@ int qc_rcv_buf(struct quic_conn *qc)
|
|||||||
*
|
*
|
||||||
* Return the socket FD or a negative error code. On error, socket is marked as
|
* Return the socket FD or a negative error code. On error, socket is marked as
|
||||||
* uninitialized.
|
* uninitialized.
|
||||||
|
* Note: This function must not be run for backends connection.
|
||||||
*/
|
*/
|
||||||
void qc_alloc_fd(struct quic_conn *qc, const struct sockaddr_storage *src,
|
void qc_alloc_fd(struct quic_conn *qc, const struct sockaddr_storage *src,
|
||||||
const struct sockaddr_storage *dst)
|
const struct sockaddr_storage *dst)
|
||||||
{
|
{
|
||||||
struct bind_conf *bc = qc->li->bind_conf;
|
struct listener *l = __objt_listener(qc->target);
|
||||||
|
struct bind_conf *bc = l->bind_conf;
|
||||||
struct proxy *p = bc->frontend;
|
struct proxy *p = bc->frontend;
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
int ret;
|
int ret;
|
||||||
@ -1013,7 +1009,7 @@ void qc_alloc_fd(struct quic_conn *qc, const struct sockaddr_storage *src,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Fallback to listener socket for this receiver instance. */
|
/* Fallback to listener socket for this receiver instance. */
|
||||||
HA_ATOMIC_STORE(&qc->li->rx.quic_mode, QUIC_SOCK_MODE_LSTNR);
|
HA_ATOMIC_STORE(&l->rx.quic_mode, QUIC_SOCK_MODE_LSTNR);
|
||||||
}
|
}
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
@ -1067,13 +1063,14 @@ struct quic_accept_queue *quic_accept_queues;
|
|||||||
void quic_accept_push_qc(struct quic_conn *qc)
|
void quic_accept_push_qc(struct quic_conn *qc)
|
||||||
{
|
{
|
||||||
struct quic_accept_queue *queue = &quic_accept_queues[tid];
|
struct quic_accept_queue *queue = &quic_accept_queues[tid];
|
||||||
struct li_per_thread *lthr = &qc->li->per_thr[ti->ltid];
|
struct listener *l = __objt_listener(qc->target);
|
||||||
|
struct li_per_thread *lthr = &l->per_thr[ti->ltid];
|
||||||
|
|
||||||
/* A connection must only be accepted once per instance. */
|
/* A connection must only be accepted once per instance. */
|
||||||
BUG_ON(qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED);
|
BUG_ON(qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED);
|
||||||
|
|
||||||
BUG_ON(MT_LIST_INLIST(&qc->accept_list));
|
BUG_ON(MT_LIST_INLIST(&qc->accept_list));
|
||||||
HA_ATOMIC_INC(&qc->li->rx.quic_curr_accept);
|
HA_ATOMIC_INC(&l->rx.quic_curr_accept);
|
||||||
|
|
||||||
qc->flags |= QUIC_FL_CONN_ACCEPT_REGISTERED;
|
qc->flags |= QUIC_FL_CONN_ACCEPT_REGISTERED;
|
||||||
/* 1. insert the listener in the accept queue
|
/* 1. insert the listener in the accept queue
|
||||||
|
159
src/quic_ssl.c
159
src/quic_ssl.c
@ -17,7 +17,7 @@ DECLARE_POOL(pool_head_quic_ssl_sock_ctx, "quic_ssl_sock_ctx", sizeof(struct ssl
|
|||||||
* be set to 1 for a QUIC server, 0 for a client.
|
* be set to 1 for a QUIC server, 0 for a client.
|
||||||
* Return 1 if succeeded, 0 if not.
|
* Return 1 if succeeded, 0 if not.
|
||||||
*/
|
*/
|
||||||
static int qc_ssl_set_quic_transport_params(struct quic_conn *qc,
|
static int qc_ssl_set_quic_transport_params(SSL *ssl, struct quic_conn *qc,
|
||||||
const struct quic_version *ver, int server)
|
const struct quic_version *ver, int server)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@ -40,7 +40,7 @@ static int qc_ssl_set_quic_transport_params(struct quic_conn *qc,
|
|||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SSL_set_quic_transport_params(qc->xprt_ctx->ssl, in, *enclen)) {
|
if (!SSL_set_quic_transport_params(ssl, in, *enclen)) {
|
||||||
TRACE_ERROR("SSL_set_quic_transport_params() failed", QUIC_EV_CONN_RWSEC);
|
TRACE_ERROR("SSL_set_quic_transport_params() failed", QUIC_EV_CONN_RWSEC);
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
@ -274,16 +274,30 @@ write:
|
|||||||
|
|
||||||
/* Set the transport parameters in the TLS stack. */
|
/* Set the transport parameters in the TLS stack. */
|
||||||
if (level == ssl_encryption_handshake && qc_is_listener(qc) &&
|
if (level == ssl_encryption_handshake && qc_is_listener(qc) &&
|
||||||
!qc_ssl_set_quic_transport_params(qc, ver, 1))
|
!qc_ssl_set_quic_transport_params(qc->xprt_ctx->ssl, qc, ver, 1))
|
||||||
goto leave;
|
goto leave;
|
||||||
|
|
||||||
keyupdate_init:
|
keyupdate_init:
|
||||||
/* Store the secret provided by the TLS stack, required for keyupdate. */
|
|
||||||
if (level == ssl_encryption_application) {
|
if (level == ssl_encryption_application) {
|
||||||
struct quic_tls_kp *prv_rx = &qc->ku.prv_rx;
|
struct quic_tls_kp *prv_rx = &qc->ku.prv_rx;
|
||||||
struct quic_tls_kp *nxt_rx = &qc->ku.nxt_rx;
|
struct quic_tls_kp *nxt_rx = &qc->ku.nxt_rx;
|
||||||
struct quic_tls_kp *nxt_tx = &qc->ku.nxt_tx;
|
struct quic_tls_kp *nxt_tx = &qc->ku.nxt_tx;
|
||||||
|
|
||||||
|
#if !defined(USE_QUIC_OPENSSL_COMPAT) && !defined(HAVE_OPENSSL_QUIC)
|
||||||
|
if (!qc_is_listener(qc)) {
|
||||||
|
const unsigned char *tp;
|
||||||
|
size_t tplen;
|
||||||
|
|
||||||
|
SSL_get_peer_quic_transport_params(ssl, &tp, &tplen);
|
||||||
|
if (!tplen || !quic_transport_params_store(qc, 1,tp, tp + tplen)) {
|
||||||
|
TRACE_ERROR("Could not parse remote transport paratemers",
|
||||||
|
QUIC_EV_CONN_RWSEC, qc);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Store the secret provided by the TLS stack, required for keyupdate. */
|
||||||
if (rx) {
|
if (rx) {
|
||||||
if (!(rx->secret = pool_alloc(pool_head_quic_tls_secret))) {
|
if (!(rx->secret = pool_alloc(pool_head_quic_tls_secret))) {
|
||||||
TRACE_ERROR("Could not allocate RX Application secrete keys", QUIC_EV_CONN_RWSEC, qc);
|
TRACE_ERROR("Could not allocate RX Application secrete keys", QUIC_EV_CONN_RWSEC, qc);
|
||||||
@ -570,7 +584,7 @@ static int ha_quic_ossl_got_transport_params(SSL *ssl, const unsigned char *para
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!quic_transport_params_store(qc, 0, params, params + params_len) ||
|
if (!quic_transport_params_store(qc, 0, params, params + params_len) ||
|
||||||
!qc_ssl_set_quic_transport_params(qc, ver, 1))
|
!qc_ssl_set_quic_transport_params(ssl, qc, ver, 1))
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -754,6 +768,38 @@ int ssl_quic_initial_ctx(struct bind_conf *bind_conf)
|
|||||||
return cfgerr;
|
return cfgerr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Allocate a TLS context for a QUIC server.
|
||||||
|
* Return this context if succeeded, NULL if failed.
|
||||||
|
*/
|
||||||
|
SSL_CTX *ssl_quic_srv_new_ssl_ctx(void)
|
||||||
|
{
|
||||||
|
SSL_CTX *ctx = NULL;
|
||||||
|
/* XXX TODO: check this: XXX */
|
||||||
|
long options =
|
||||||
|
(SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
|
||||||
|
SSL_OP_SINGLE_ECDH_USE |
|
||||||
|
SSL_OP_CIPHER_SERVER_PREFERENCE;
|
||||||
|
|
||||||
|
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
|
/* This function gives the detail of the SSL error. It is used only
|
||||||
* if the debug mode and the verbose mode are activated. It dump all
|
* if the debug mode and the verbose mode are activated. It dump all
|
||||||
* the SSL error until the stack was empty.
|
* the SSL error until the stack was empty.
|
||||||
@ -885,28 +931,54 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
|
|||||||
* provided by the stack. This happens after having received the peer
|
* provided by the stack. This happens after having received the peer
|
||||||
* handshake level CRYPTO data which are validated by the TLS stack.
|
* handshake level CRYPTO data which are validated by the TLS stack.
|
||||||
*/
|
*/
|
||||||
if (qc->li->bind_conf->ssl_conf.early_data &&
|
if (qc_is_listener(qc)) {
|
||||||
(!qc->ael || !qc->ael->tls_ctx.rx.secret)) {
|
if (__objt_listener(qc->target)->bind_conf->ssl_conf.early_data &&
|
||||||
TRACE_PROTO("SSL handshake in progress",
|
(!qc->ael || !qc->ael->tls_ctx.rx.secret)) {
|
||||||
QUIC_EV_CONN_IO_CB, qc, &state, &ssl_err);
|
TRACE_PROTO("SSL handshake in progress",
|
||||||
goto out;
|
QUIC_EV_CONN_IO_CB, qc, &state, &ssl_err);
|
||||||
}
|
goto out;
|
||||||
else {
|
}
|
||||||
TRACE_PROTO("SSL handshake OK", QUIC_EV_CONN_IO_CB, qc, &state);
|
else {
|
||||||
|
TRACE_PROTO("SSL handshake OK", QUIC_EV_CONN_IO_CB, qc, &state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Check the alpn could be negotiated */
|
/* Check the alpn could be negotiated */
|
||||||
if (!qc->app_ops) {
|
if (qc_is_listener(qc)) {
|
||||||
TRACE_ERROR("No negotiated ALPN", QUIC_EV_CONN_IO_CB, qc, &state);
|
if (!qc->app_ops) {
|
||||||
quic_set_tls_alert(qc, SSL_AD_NO_APPLICATION_PROTOCOL);
|
TRACE_ERROR("No negotiated ALPN", QUIC_EV_CONN_IO_CB, qc, &state);
|
||||||
goto leave;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* I/O callback switch */
|
qc->flags |= QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS;
|
||||||
qc->wait_event.tasklet->process = quic_conn_app_io_cb;
|
|
||||||
if (qc_is_listener(ctx->qc)) {
|
if (qc_is_listener(ctx->qc)) {
|
||||||
qc->flags |= QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS;
|
struct listener *l = __objt_listener(qc->target);
|
||||||
|
/* I/O callback switch */
|
||||||
|
qc->wait_event.tasklet->process = quic_conn_app_io_cb;
|
||||||
qc->state = QUIC_HS_ST_CONFIRMED;
|
qc->state = QUIC_HS_ST_CONFIRMED;
|
||||||
|
|
||||||
if (!(qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED)) {
|
if (!(qc->flags & QUIC_FL_CONN_ACCEPT_REGISTERED)) {
|
||||||
@ -920,8 +992,8 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
|
|||||||
tasklet_wakeup(qc->wait_event.tasklet);
|
tasklet_wakeup(qc->wait_event.tasklet);
|
||||||
}
|
}
|
||||||
|
|
||||||
BUG_ON(qc->li->rx.quic_curr_handshake == 0);
|
BUG_ON(l->rx.quic_curr_handshake == 0);
|
||||||
HA_ATOMIC_DEC(&qc->li->rx.quic_curr_handshake);
|
HA_ATOMIC_DEC(&l->rx.quic_curr_handshake);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
qc->state = QUIC_HS_ST_COMPLETE;
|
qc->state = QUIC_HS_ST_COMPLETE;
|
||||||
@ -1041,7 +1113,8 @@ int quic_ssl_set_tls_cbs(SSL *ssl)
|
|||||||
* Return 0 if succeeded, -1 if not. If failed, sets the ->err_code member of <qc->conn> to
|
* Return 0 if succeeded, -1 if not. If failed, sets the ->err_code member of <qc->conn> to
|
||||||
* CO_ER_SSL_NO_MEM.
|
* CO_ER_SSL_NO_MEM.
|
||||||
*/
|
*/
|
||||||
static int qc_ssl_sess_init(struct quic_conn *qc, SSL_CTX *ssl_ctx, SSL **ssl)
|
static int qc_ssl_sess_init(struct quic_conn *qc, SSL_CTX *ssl_ctx, SSL **ssl,
|
||||||
|
struct connection *conn, int server)
|
||||||
{
|
{
|
||||||
int retry, ret = -1;
|
int retry, ret = -1;
|
||||||
|
|
||||||
@ -1059,6 +1132,7 @@ static int qc_ssl_sess_init(struct quic_conn *qc, SSL_CTX *ssl_ctx, SSL **ssl)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!SSL_set_ex_data(*ssl, ssl_qc_app_data_index, qc) ||
|
if (!SSL_set_ex_data(*ssl, ssl_qc_app_data_index, qc) ||
|
||||||
|
(!server && !SSL_set_ex_data(*ssl, ssl_app_data_index, conn)) ||
|
||||||
!quic_ssl_set_tls_cbs(*ssl)) {
|
!quic_ssl_set_tls_cbs(*ssl)) {
|
||||||
SSL_free(*ssl);
|
SSL_free(*ssl);
|
||||||
*ssl = NULL;
|
*ssl = NULL;
|
||||||
@ -1121,10 +1195,9 @@ static int qc_set_quic_early_data_enabled(struct quic_conn *qc, SSL *ssl)
|
|||||||
*
|
*
|
||||||
* Returns 0 on success else non-zero.
|
* Returns 0 on success else non-zero.
|
||||||
*/
|
*/
|
||||||
int qc_alloc_ssl_sock_ctx(struct quic_conn *qc)
|
int qc_alloc_ssl_sock_ctx(struct quic_conn *qc, struct connection *conn)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct bind_conf *bc = qc->li->bind_conf;
|
|
||||||
struct ssl_sock_ctx *ctx = NULL;
|
struct ssl_sock_ctx *ctx = NULL;
|
||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
|
TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
|
||||||
@ -1135,7 +1208,7 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->conn = NULL;
|
ctx->conn = conn;
|
||||||
ctx->bio = NULL;
|
ctx->bio = NULL;
|
||||||
ctx->xprt = NULL;
|
ctx->xprt = NULL;
|
||||||
ctx->xprt_ctx = NULL;
|
ctx->xprt_ctx = NULL;
|
||||||
@ -1148,7 +1221,9 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc)
|
|||||||
ctx->qc = qc;
|
ctx->qc = qc;
|
||||||
|
|
||||||
if (qc_is_listener(qc)) {
|
if (qc_is_listener(qc)) {
|
||||||
if (qc_ssl_sess_init(qc, bc->initial_ctx, &ctx->ssl) == -1)
|
struct bind_conf *bc = __objt_listener(qc->target)->bind_conf;
|
||||||
|
|
||||||
|
if (qc_ssl_sess_init(qc, bc->initial_ctx, &ctx->ssl, NULL, 1) == -1)
|
||||||
goto err;
|
goto err;
|
||||||
#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L) && defined(HAVE_SSL_0RTT_QUIC)
|
#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L) && defined(HAVE_SSL_0RTT_QUIC)
|
||||||
/* Enabling 0-RTT */
|
/* Enabling 0-RTT */
|
||||||
@ -1158,6 +1233,36 @@ int qc_alloc_ssl_sock_ctx(struct quic_conn *qc)
|
|||||||
|
|
||||||
SSL_set_accept_state(ctx->ssl);
|
SSL_set_accept_state(ctx->ssl);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
int ssl_err;
|
||||||
|
struct server *srv = __objt_server(ctx->conn->target);
|
||||||
|
|
||||||
|
if (qc_ssl_sess_init(qc, srv->ssl_ctx.ctx, &ctx->ssl, conn, 0) == -1)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (!qc_ssl_set_quic_transport_params(ctx->ssl, qc, quic_version_1, 0))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
SSL_set_connect_state(ctx->ssl);
|
||||||
|
ssl_err = SSL_do_handshake(ctx->ssl);
|
||||||
|
TRACE_PROTO("SSL_do_handshake() called", QUIC_EV_CONN_NEW, qc, &ssl_err);
|
||||||
|
if (ssl_err != 1) {
|
||||||
|
ssl_err = SSL_get_error(ctx->ssl, ssl_err);
|
||||||
|
if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE) {
|
||||||
|
TRACE_PROTO("SSL handshake in progress", QUIC_EV_CONN_NEW, qc, &ssl_err);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
TRACE_ERROR("SSL handshake error", QUIC_EV_CONN_NEW, qc, &ssl_err);
|
||||||
|
HA_ATOMIC_INC(&qc->prx_counters->hdshk_fail);
|
||||||
|
qc_ssl_dump_errors(ctx->conn);
|
||||||
|
ERR_clear_error();
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wakeup the handshake I/O handler tasklet asap to send data */
|
||||||
|
tasklet_wakeup(qc->wait_event.tasklet);
|
||||||
|
}
|
||||||
|
|
||||||
ctx->xprt = xprt_get(XPRT_QUIC);
|
ctx->xprt = xprt_get(XPRT_QUIC);
|
||||||
|
|
||||||
|
@ -213,9 +213,18 @@ static int quic_transport_param_dec_version_info(struct tp_version_information *
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server)
|
if (server) {
|
||||||
/* TODO: not supported */
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < quic_versions_nb; i++) {
|
||||||
|
if (tp->chosen == quic_versions[i].num) {
|
||||||
|
tp->negotiated_version = &quic_versions[i];
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
for (ver = others; ver < end; ver += 4) {
|
for (ver = others; ver < end; ver += 4) {
|
||||||
if (!tp->negotiated_version) {
|
if (!tp->negotiated_version) {
|
||||||
@ -513,8 +522,21 @@ static int quic_transport_param_enc_version_info(unsigned char **buf,
|
|||||||
memcpy(*buf, &ver, sizeof ver);
|
memcpy(*buf, &ver, sizeof ver);
|
||||||
*buf += sizeof ver;
|
*buf += sizeof ver;
|
||||||
/* For servers: all supported version, chosen included */
|
/* For servers: all supported version, chosen included */
|
||||||
for (i = 0; i < quic_versions_nb; i++) {
|
if (server) {
|
||||||
ver = htonl(quic_versions[i].num);
|
for (i = 0; i < quic_versions_nb; i++) {
|
||||||
|
ver = htonl(quic_versions[i].num);
|
||||||
|
memcpy(*buf, &ver, sizeof ver);
|
||||||
|
*buf += sizeof ver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ver = htonl(quic_version_1->num);
|
||||||
|
memcpy(*buf, &ver, sizeof ver);
|
||||||
|
*buf += sizeof ver;
|
||||||
|
ver = htonl(quic_version_2->num);
|
||||||
|
memcpy(*buf, &ver, sizeof ver);
|
||||||
|
*buf += sizeof ver;
|
||||||
|
ver = htonl(quic_version_draft_29->num);
|
||||||
memcpy(*buf, &ver, sizeof ver);
|
memcpy(*buf, &ver, sizeof ver);
|
||||||
*buf += sizeof ver;
|
*buf += sizeof ver;
|
||||||
}
|
}
|
||||||
@ -806,3 +828,21 @@ int qc_lstnr_params_init(struct quic_conn *qc,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* QUIC client (or haproxy server) only function.
|
||||||
|
* Initialize the local transport parameters <rx_params> from <srv_params>
|
||||||
|
* coming from configuration and source connection ID).
|
||||||
|
* Never fails.
|
||||||
|
*/
|
||||||
|
void qc_srv_params_init(struct quic_conn *qc,
|
||||||
|
const struct quic_transport_params *srv_params,
|
||||||
|
const unsigned char *scid, size_t scidlen)
|
||||||
|
{
|
||||||
|
struct quic_transport_params *rx_params = &qc->rx.params;
|
||||||
|
|
||||||
|
/* Copy the transport parameters. */
|
||||||
|
*rx_params = *srv_params;
|
||||||
|
/* Copy the initial source connection ID. */
|
||||||
|
memcpy(rx_params->initial_source_connection_id.data, scid, scidlen);
|
||||||
|
rx_params->initial_source_connection_id.len = scidlen;
|
||||||
|
TRACE_PROTO("\nRX(local) transp. params.", QUIC_EV_TRANSP_PARAMS, qc, rx_params);
|
||||||
|
}
|
||||||
|
@ -117,6 +117,13 @@ static void quic_trace(enum trace_level level, uint64_t mask, const struct trace
|
|||||||
|
|
||||||
chunk_appendf(&trace_buf, " : qc@%p idle_timer_task@%p flags=0x%x",
|
chunk_appendf(&trace_buf, " : qc@%p idle_timer_task@%p flags=0x%x",
|
||||||
qc, qc->idle_timer_task, qc->flags);
|
qc, qc->idle_timer_task, qc->flags);
|
||||||
|
if (mask & QUIC_EV_CONN_NEW) {
|
||||||
|
const int *ssl_err = a2;
|
||||||
|
|
||||||
|
if (ssl_err)
|
||||||
|
chunk_appendf(&trace_buf, " ssl_err=%d", *ssl_err);
|
||||||
|
}
|
||||||
|
|
||||||
if (mask & QUIC_EV_CONN_INIT) {
|
if (mask & QUIC_EV_CONN_INIT) {
|
||||||
chunk_appendf(&trace_buf, "\n odcid");
|
chunk_appendf(&trace_buf, "\n odcid");
|
||||||
quic_cid_dump(&trace_buf, &qc->odcid);
|
quic_cid_dump(&trace_buf, &qc->odcid);
|
||||||
|
@ -288,8 +288,10 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx)
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct quic_conn *qc;
|
struct quic_conn *qc;
|
||||||
char skip_sendto = 0;
|
char skip_sendto = 0;
|
||||||
|
struct listener *l;
|
||||||
|
|
||||||
qc = ctx->qc;
|
qc = ctx->qc;
|
||||||
|
l = objt_listener(qc->target);
|
||||||
TRACE_ENTER(QUIC_EV_CONN_SPPKTS, qc);
|
TRACE_ENTER(QUIC_EV_CONN_SPPKTS, qc);
|
||||||
while (b_contig_data(buf, 0)) {
|
while (b_contig_data(buf, 0)) {
|
||||||
unsigned char *pos;
|
unsigned char *pos;
|
||||||
@ -305,7 +307,11 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx)
|
|||||||
|
|
||||||
/* If datagram bigger than MTU, several ones were encoded for GSO usage. */
|
/* If datagram bigger than MTU, several ones were encoded for GSO usage. */
|
||||||
if (dglen > qc->path->mtu) {
|
if (dglen > qc->path->mtu) {
|
||||||
if (likely(!(HA_ATOMIC_LOAD(&qc->li->flags) & LI_F_UDP_GSO_NOTSUPP))) {
|
/* TODO: note that at this time for connection to backends this
|
||||||
|
* part is not run because no more than an MTU has been prepared for
|
||||||
|
* such connections (dglen <= qc->path->mtu). So, here l is not NULL.
|
||||||
|
*/
|
||||||
|
if (likely(!(HA_ATOMIC_LOAD(&l->flags) & LI_F_UDP_GSO_NOTSUPP))) {
|
||||||
TRACE_PROTO("send multiple datagrams with GSO", QUIC_EV_CONN_SPPKTS, qc);
|
TRACE_PROTO("send multiple datagrams with GSO", QUIC_EV_CONN_SPPKTS, qc);
|
||||||
gso = qc->path->mtu;
|
gso = qc->path->mtu;
|
||||||
}
|
}
|
||||||
@ -327,11 +333,15 @@ static int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx)
|
|||||||
int ret = qc_snd_buf(qc, &tmpbuf, tmpbuf.data, 0, gso);
|
int ret = qc_snd_buf(qc, &tmpbuf, tmpbuf.data, 0, gso);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (gso && ret == -EIO) {
|
if (gso && ret == -EIO) {
|
||||||
|
/* TODO: note that at this time for connection to backends this
|
||||||
|
* part is not run because no more than an MTU has been
|
||||||
|
* prepared for such connections (l is not NULL).
|
||||||
|
*/
|
||||||
/* Disable permanently UDP GSO for this listener.
|
/* Disable permanently UDP GSO for this listener.
|
||||||
* Retry standard emission.
|
* Retry standard emission.
|
||||||
*/
|
*/
|
||||||
TRACE_ERROR("mark listener UDP GSO as unsupported", QUIC_EV_CONN_SPPKTS, qc, first_pkt);
|
TRACE_ERROR("mark listener UDP GSO as unsupported", QUIC_EV_CONN_SPPKTS, qc, first_pkt);
|
||||||
HA_ATOMIC_OR(&qc->li->flags, LI_F_UDP_GSO_NOTSUPP);
|
HA_ATOMIC_OR(&l->flags, LI_F_UDP_GSO_NOTSUPP);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -576,6 +586,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
|
|||||||
int dgram_cnt = 0;
|
int dgram_cnt = 0;
|
||||||
/* Restrict GSO emission to comply with sendmsg limitation. See QUIC_MAX_GSO_DGRAMS for more details. */
|
/* Restrict GSO emission to comply with sendmsg limitation. See QUIC_MAX_GSO_DGRAMS for more details. */
|
||||||
uchar gso_dgram_cnt = 0;
|
uchar gso_dgram_cnt = 0;
|
||||||
|
struct listener *l = objt_listener(qc->target);
|
||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
|
TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
|
||||||
/* Currently qc_prep_pkts() does not handle buffer wrapping so the
|
/* Currently qc_prep_pkts() does not handle buffer wrapping so the
|
||||||
@ -765,11 +776,13 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
|
|||||||
prv_pkt = cur_pkt;
|
prv_pkt = cur_pkt;
|
||||||
}
|
}
|
||||||
else if (!(quic_tune.options & QUIC_TUNE_NO_UDP_GSO) &&
|
else if (!(quic_tune.options & QUIC_TUNE_NO_UDP_GSO) &&
|
||||||
!(HA_ATOMIC_LOAD(&qc->li->flags) & LI_F_UDP_GSO_NOTSUPP) &&
|
|
||||||
dglen == qc->path->mtu &&
|
dglen == qc->path->mtu &&
|
||||||
(char *)end < b_wrap(buf) &&
|
(char *)end < b_wrap(buf) &&
|
||||||
++gso_dgram_cnt < QUIC_MAX_GSO_DGRAMS) {
|
++gso_dgram_cnt < QUIC_MAX_GSO_DGRAMS &&
|
||||||
|
l && !(HA_ATOMIC_LOAD(&l->flags) & LI_F_UDP_GSO_NOTSUPP)) {
|
||||||
|
/* TODO: note that for backends GSO is not used. No more than
|
||||||
|
* an MTU is prepared.
|
||||||
|
*/
|
||||||
/* A datagram covering the full MTU has been
|
/* A datagram covering the full MTU has been
|
||||||
* built, use GSO to built next entry. Do not
|
* built, use GSO to built next entry. Do not
|
||||||
* reserve extra space for datagram header.
|
* reserve extra space for datagram header.
|
||||||
|
26
src/server.c
26
src/server.c
@ -37,6 +37,7 @@
|
|||||||
#include <haproxy/protocol.h>
|
#include <haproxy/protocol.h>
|
||||||
#include <haproxy/proxy.h>
|
#include <haproxy/proxy.h>
|
||||||
#include <haproxy/queue.h>
|
#include <haproxy/queue.h>
|
||||||
|
#include <haproxy/quic_tp.h>
|
||||||
#include <haproxy/resolvers.h>
|
#include <haproxy/resolvers.h>
|
||||||
#include <haproxy/sample.h>
|
#include <haproxy/sample.h>
|
||||||
#include <haproxy/sc_strm.h>
|
#include <haproxy/sc_strm.h>
|
||||||
@ -3589,6 +3590,7 @@ static int _srv_parse_init(struct server **srv, char **args, int *cur_arg,
|
|||||||
(parse_flags & SRV_PARSE_INITIAL_RESOLVE ? PA_O_RESOLVE : 0) | PA_O_PORT_OK |
|
(parse_flags & SRV_PARSE_INITIAL_RESOLVE ? PA_O_RESOLVE : 0) | PA_O_PORT_OK |
|
||||||
(parse_flags & SRV_PARSE_IN_PEER_SECTION ? PA_O_PORT_MAND : PA_O_PORT_OFS) |
|
(parse_flags & SRV_PARSE_IN_PEER_SECTION ? PA_O_PORT_MAND : PA_O_PORT_OFS) |
|
||||||
PA_O_STREAM | PA_O_DGRAM | PA_O_XPRT);
|
PA_O_STREAM | PA_O_DGRAM | PA_O_XPRT);
|
||||||
|
|
||||||
if (!sk) {
|
if (!sk) {
|
||||||
ha_alert("%s\n", errmsg);
|
ha_alert("%s\n", errmsg);
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
@ -3596,6 +3598,21 @@ static int _srv_parse_init(struct server **srv, char **args, int *cur_arg,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_QUIC
|
||||||
|
if (srv_is_quic(newsrv)) {
|
||||||
|
if (!experimental_directives_allowed) {
|
||||||
|
ha_alert("QUIC is experimental for server '%s',"
|
||||||
|
" must be allowed via a global 'expose-experimental-directives'\n",
|
||||||
|
newsrv->id);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
newsrv->xprt = xprt_get(XPRT_QUIC);
|
||||||
|
quic_transport_params_init(&newsrv->quic_params, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!port1 || !port2) {
|
if (!port1 || !port2) {
|
||||||
if (sk->ss_family != AF_CUST_RHTTP_SRV) {
|
if (sk->ss_family != AF_CUST_RHTTP_SRV) {
|
||||||
/* no port specified, +offset, -offset */
|
/* no port specified, +offset, -offset */
|
||||||
@ -3819,6 +3836,15 @@ 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);
|
srv_lb_commit_status(srv);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -265,7 +265,8 @@ static int sock_handle_system_err(struct connection *conn, struct proxy *be)
|
|||||||
* upper level is set as SF_ERR_NONE; -1 on failure, stream_err is set to
|
* upper level is set as SF_ERR_NONE; -1 on failure, stream_err is set to
|
||||||
* appropriate value.
|
* appropriate value.
|
||||||
*/
|
*/
|
||||||
int sock_create_server_socket(struct connection *conn, struct proxy *be, int *stream_err)
|
int sock_create_server_socket(struct connection *conn, struct proxy *be,
|
||||||
|
enum proto_type proto_type, int sock_type, int *stream_err)
|
||||||
{
|
{
|
||||||
const struct netns_entry *ns = NULL;
|
const struct netns_entry *ns = NULL;
|
||||||
const struct protocol *proto;
|
const struct protocol *proto;
|
||||||
@ -279,9 +280,9 @@ int sock_create_server_socket(struct connection *conn, struct proxy *be, int *st
|
|||||||
ns = __objt_server(conn->target)->netns;
|
ns = __objt_server(conn->target)->netns;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
proto = protocol_lookup(conn->dst->ss_family, PROTO_TYPE_STREAM, conn->ctrl->sock_prot == IPPROTO_MPTCP);
|
proto = protocol_lookup(conn->dst->ss_family, proto_type , conn->ctrl->sock_prot == IPPROTO_MPTCP);
|
||||||
BUG_ON(!proto);
|
BUG_ON(!proto);
|
||||||
sock_fd = my_socketat(ns, proto->fam->sock_domain, SOCK_STREAM, proto->sock_prot);
|
sock_fd = my_socketat(ns, proto->fam->sock_domain, sock_type, proto->sock_prot);
|
||||||
|
|
||||||
/* at first, handle common to all proto families system limits and permission related errors */
|
/* at first, handle common to all proto families system limits and permission related errors */
|
||||||
if (sock_fd == -1) {
|
if (sock_fd == -1) {
|
||||||
|
@ -2601,8 +2601,9 @@ int ckch_inst_rebuild(struct ckch_store *ckch_store, struct ckch_inst *ckchi,
|
|||||||
fcount = ckchi->crtlist_entry->fcount;
|
fcount = ckchi->crtlist_entry->fcount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ckchi->is_server_instance)
|
if (ckchi->is_server_instance) {
|
||||||
errcode |= ckch_inst_new_load_srv_store(ckch_store->path, ckch_store, new_inst, err);
|
errcode |= ckch_inst_new_load_srv_store(ckch_store->path, ckch_store, new_inst, err, srv_is_quic(ckchi->server));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
errcode |= ckch_inst_new_load_store(ckch_store->path, ckch_store, ckchi->bind_conf, ckchi->ssl_conf, sni_filter, fcount, ckchi->is_default, new_inst, err);
|
errcode |= ckch_inst_new_load_store(ckch_store->path, ckch_store, ckchi->bind_conf, ckchi->ssl_conf, sni_filter, fcount, ckchi->is_default, new_inst, err);
|
||||||
|
|
||||||
|
@ -177,7 +177,7 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *arg)
|
|||||||
s = __objt_listener(conn->target)->bind_conf;
|
s = __objt_listener(conn->target)->bind_conf;
|
||||||
#ifdef USE_QUIC
|
#ifdef USE_QUIC
|
||||||
else if (qc)
|
else if (qc)
|
||||||
s = qc->li->bind_conf;
|
s = __objt_listener(qc->target)->bind_conf;
|
||||||
#endif /* USE_QUIC */
|
#endif /* USE_QUIC */
|
||||||
|
|
||||||
if (!s) {
|
if (!s) {
|
||||||
|
@ -127,7 +127,7 @@ int ssl_sock_ocsp_stapling_cbk(SSL *ssl, void *arg)
|
|||||||
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
||||||
|
|
||||||
/* null if not a listener */
|
/* null if not a listener */
|
||||||
li = qc->li;
|
li = objt_listener(qc->target);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -65,6 +65,7 @@
|
|||||||
#include <haproxy/proxy.h>
|
#include <haproxy/proxy.h>
|
||||||
#include <haproxy/quic_conn.h>
|
#include <haproxy/quic_conn.h>
|
||||||
#include <haproxy/quic_openssl_compat.h>
|
#include <haproxy/quic_openssl_compat.h>
|
||||||
|
#include <haproxy/quic_ssl.h>
|
||||||
#include <haproxy/quic_tp.h>
|
#include <haproxy/quic_tp.h>
|
||||||
#include <haproxy/sample.h>
|
#include <haproxy/sample.h>
|
||||||
#include <haproxy/sc_strm.h>
|
#include <haproxy/sc_strm.h>
|
||||||
@ -925,7 +926,7 @@ static int ssl_tlsext_ticket_key_cb(SSL *s, unsigned char key_name[16], unsigned
|
|||||||
ref = __objt_listener(conn->target)->bind_conf->keys_ref;
|
ref = __objt_listener(conn->target)->bind_conf->keys_ref;
|
||||||
#ifdef USE_QUIC
|
#ifdef USE_QUIC
|
||||||
else if (qc)
|
else if (qc)
|
||||||
ref = qc->li->bind_conf->keys_ref;
|
ref = __objt_listener(qc->target)->bind_conf->keys_ref;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!ref) {
|
if (!ref) {
|
||||||
@ -1481,7 +1482,7 @@ int ssl_sock_bind_verifycbk(int ok, X509_STORE_CTX *x_store)
|
|||||||
else {
|
else {
|
||||||
qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
|
||||||
BUG_ON(!qc); /* Must never happen */
|
BUG_ON(!qc); /* Must never happen */
|
||||||
bind_conf = qc->li->bind_conf;
|
bind_conf = __objt_listener(qc->target)->bind_conf;
|
||||||
ctx = qc->xprt_ctx;
|
ctx = qc->xprt_ctx;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -3039,6 +3040,20 @@ error:
|
|||||||
return errcode;
|
return errcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_QUIC
|
||||||
|
static inline SSL_CTX *ssl_sock_new_ssl_ctx(int is_quic)
|
||||||
|
{
|
||||||
|
if (is_quic)
|
||||||
|
return ssl_quic_srv_new_ssl_ctx();
|
||||||
|
else
|
||||||
|
return SSL_CTX_new(SSLv23_client_method());
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline SSL_CTX *ssl_sock_new_ssl_ctx(int is_quic)
|
||||||
|
{
|
||||||
|
return SSL_CTX_new(SSLv23_client_method());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function allocate a ckch_inst that will be used on the backend side
|
* This function allocate a ckch_inst that will be used on the backend side
|
||||||
@ -3050,7 +3065,7 @@ error:
|
|||||||
* ERR_WARN if a warning is available into err
|
* ERR_WARN if a warning is available into err
|
||||||
*/
|
*/
|
||||||
int ckch_inst_new_load_srv_store(const char *path, struct ckch_store *ckchs,
|
int ckch_inst_new_load_srv_store(const char *path, struct ckch_store *ckchs,
|
||||||
struct ckch_inst **ckchi, char **err)
|
struct ckch_inst **ckchi, char **err, int is_quic)
|
||||||
{
|
{
|
||||||
SSL_CTX *ctx;
|
SSL_CTX *ctx;
|
||||||
struct ckch_data *data;
|
struct ckch_data *data;
|
||||||
@ -3064,7 +3079,7 @@ int ckch_inst_new_load_srv_store(const char *path, struct ckch_store *ckchs,
|
|||||||
|
|
||||||
data = ckchs->data;
|
data = ckchs->data;
|
||||||
|
|
||||||
ctx = SSL_CTX_new(SSLv23_client_method());
|
ctx = ssl_sock_new_ssl_ctx(is_quic);
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
memprintf(err, "%sunable to allocate SSL context for cert '%s'.\n",
|
memprintf(err, "%sunable to allocate SSL context for cert '%s'.\n",
|
||||||
err && *err ? *err : "", path);
|
err && *err ? *err : "", path);
|
||||||
@ -3135,7 +3150,8 @@ static int ssl_sock_load_srv_ckchs(const char *path, struct ckch_store *ckchs,
|
|||||||
int errcode = 0;
|
int errcode = 0;
|
||||||
|
|
||||||
/* we found the ckchs in the tree, we can use it directly */
|
/* we found the ckchs in the tree, we can use it directly */
|
||||||
errcode |= ckch_inst_new_load_srv_store(path, ckchs, ckch_inst, err);
|
errcode |= ckch_inst_new_load_srv_store(path, ckchs, ckch_inst, err,
|
||||||
|
srv_is_quic(server));
|
||||||
|
|
||||||
if (errcode & ERR_CODE)
|
if (errcode & ERR_CODE)
|
||||||
return errcode;
|
return errcode;
|
||||||
@ -4398,7 +4414,9 @@ int ssl_sock_prepare_srv_ctx(struct server *srv)
|
|||||||
return cfgerr;
|
return cfgerr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (srv->use_ssl == 1)
|
|
||||||
|
/* The QUIC server xprt has already been set. */
|
||||||
|
if (srv->use_ssl == 1 && !srv_is_quic(srv))
|
||||||
srv->xprt = &ssl_sock;
|
srv->xprt = &ssl_sock;
|
||||||
|
|
||||||
if (srv->ssl_ctx.client_crt) {
|
if (srv->ssl_ctx.client_crt) {
|
||||||
@ -4425,7 +4443,7 @@ int ssl_sock_prepare_srv_ctx(struct server *srv)
|
|||||||
/* The context will be uninitialized if there wasn't any "cert" option
|
/* The context will be uninitialized if there wasn't any "cert" option
|
||||||
* in the server line. */
|
* in the server line. */
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
ctx = SSL_CTX_new(SSLv23_client_method());
|
ctx = ssl_sock_new_ssl_ctx(srv_is_quic(srv));
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
ha_alert("unable to allocate ssl context.\n");
|
ha_alert("unable to allocate ssl context.\n");
|
||||||
cfgerr++;
|
cfgerr++;
|
||||||
|
@ -111,10 +111,25 @@ static int quic_conn_unsubscribe(struct connection *conn, void *xprt_ctx, int ev
|
|||||||
*/
|
*/
|
||||||
static int qc_conn_init(struct connection *conn, void **xprt_ctx)
|
static int qc_conn_init(struct connection *conn, void **xprt_ctx)
|
||||||
{
|
{
|
||||||
struct quic_conn *qc = conn->handle.qc;
|
int ret = -1;
|
||||||
|
struct quic_conn *qc = NULL;
|
||||||
|
|
||||||
TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
|
TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
|
||||||
|
|
||||||
|
if (objt_listener(conn->target)) {
|
||||||
|
qc = conn->handle.qc;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int ipv4 = conn->dst->ss_family == AF_INET;
|
||||||
|
struct server *srv = objt_server(conn->target);
|
||||||
|
qc = qc_new_conn(quic_version_1, ipv4, NULL, NULL, NULL,
|
||||||
|
NULL, NULL, &srv->addr, 0, srv, conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!qc)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
/* Ensure thread connection migration is finalized ASAP. */
|
/* Ensure thread connection migration is finalized ASAP. */
|
||||||
if (qc->flags & QUIC_FL_CONN_TID_REBIND)
|
if (qc->flags & QUIC_FL_CONN_TID_REBIND)
|
||||||
qc_finalize_tid_rebind(qc);
|
qc_finalize_tid_rebind(qc);
|
||||||
@ -128,7 +143,7 @@ static int qc_conn_init(struct connection *conn, void **xprt_ctx)
|
|||||||
out:
|
out:
|
||||||
TRACE_LEAVE(QUIC_EV_CONN_NEW, qc);
|
TRACE_LEAVE(QUIC_EV_CONN_NEW, qc);
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start the QUIC transport layer */
|
/* Start the QUIC transport layer */
|
||||||
@ -140,8 +155,13 @@ static int qc_xprt_start(struct connection *conn, void *ctx)
|
|||||||
qc = conn->handle.qc;
|
qc = conn->handle.qc;
|
||||||
TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
|
TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
|
||||||
|
|
||||||
/* mux-quic can now be considered ready. */
|
if (objt_listener(conn->target)) {
|
||||||
qc->mux_state = QC_MUX_READY;
|
/* mux-quic can now be considered ready. */
|
||||||
|
qc->mux_state = QC_MUX_READY;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
conn->flags |= CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN;
|
||||||
|
}
|
||||||
|
|
||||||
/* Schedule quic-conn to ensure post handshake frames are emitted. This
|
/* Schedule quic-conn to ensure post handshake frames are emitted. This
|
||||||
* is not done for 0-RTT as xprt->start happens before handshake
|
* is not done for 0-RTT as xprt->start happens before handshake
|
||||||
@ -178,6 +198,8 @@ static struct xprt_ops ssl_quic = {
|
|||||||
.start = qc_xprt_start,
|
.start = qc_xprt_start,
|
||||||
.prepare_bind_conf = ssl_sock_prepare_bind_conf,
|
.prepare_bind_conf = ssl_sock_prepare_bind_conf,
|
||||||
.destroy_bind_conf = ssl_sock_destroy_bind_conf,
|
.destroy_bind_conf = ssl_sock_destroy_bind_conf,
|
||||||
|
.prepare_srv = ssl_sock_prepare_srv_ctx,
|
||||||
|
.destroy_srv = ssl_sock_free_srv_ctx,
|
||||||
.get_alpn = ssl_sock_get_alpn,
|
.get_alpn = ssl_sock_get_alpn,
|
||||||
.get_ssl_sock_ctx = qc_get_ssl_sock_ctx,
|
.get_ssl_sock_ctx = qc_get_ssl_sock_ctx,
|
||||||
.dump_info = qc_xprt_dump_info,
|
.dump_info = qc_xprt_dump_info,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user