Commit Graph

17828 Commits

Author SHA1 Message Date
Aurelien DARRAGON
6b16cfe38b BUG/MINOR: proxy: fix dyncookie_key leak on deinit()
proxy dyncookie_key wasn't cleaned up in free_proxy(), resulting in small
memory leak if "dynamic-cookie-key" was used on a regular or default
proxy.

It may be backported to all stable versions.

(cherry picked from commit 6f53df3fcfe3dc7220cff862d3ded1601f642931)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
2024-06-12 09:06:55 +02:00
Aurelien DARRAGON
7680c52b16 BUG/MINOR: proxy: fix check_{command,path} leak on deinit()
proxy check_{command,path} members (used for "external-check" feature)
weren't cleaned up in free_proxy(), resulting in small memory leak if
"external-check command" or "external-check path" were used on a regular
or default proxy.

It may be backported to all stable versions.

(cherry picked from commit 62d0465a96ac847f40e95a9474d8971dac062114)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
2024-06-12 09:06:52 +02:00
Aurelien DARRAGON
28d69a218e BUG/MINOR: proxy: fix email-alert leak on deinit()
proxy email-alert settings weren't cleaned up in free_proxy(), resulting
in small memory leak if "email-alert to" or "email-alert from" were used
on a regular or default proxy.

It may be backported to all stable versions.

(cherry picked from commit fa90a7d313703cfc4e1b41d258d0d6d470ffe967)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
2024-06-12 09:06:48 +02:00
Aurelien DARRAGON
cc722a3e5b BUG/MINOR: proxy: fix log_tag leak on deinit()
proxy log_tag wasn't cleaned up in free_proxy(), resulting in small
memory leak if "log-tag" was used on a regular or default proxy.

It may be backported to all stable versions.

(cherry picked from commit 77b192ea3682a2ad56418d2e4379bb763e0b427e)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
2024-06-12 09:06:45 +02:00
Aurelien DARRAGON
67d4a9507d BUG/MINOR: proxy: fix server_id_hdr_name leak on deinit()
proxy server_id_hdr_name member (used for "http-send-name-header" option)
wasn't cleaned up in free_proxy(), resulting in small memory leak if
"http-send-name-header" was used on a regular or default proxy.

This may be backported to all stable versions.

(cherry picked from commit 99f340958211881dc6b48b6dc960d126179c4bcc)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
2024-06-12 09:06:39 +02:00
Aurelien DARRAGON
948a83f4e9 MINOR: log: fix "http-send-name-header" ignore warning message
Warning message to indicate that the "http-send-name-header" option is
ignored for backend in "mode log" was referenced using its internal
struct wording instead of public name (as seen in the documentation).

Let's fix that.

It may be backported with c7783fb ("MINOR: log/backend: prevent
"http-send-name-header" use with LOG mode") in 2.9.

(cherry picked from commit e5ccfda9d31db54dfafe618404e79f09883b3ea5)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
2024-06-12 09:06:32 +02:00
Christopher Faulet
200734201f BUG/MINOR: mux-h1: Use the right variable to set NEGO_FF_FL_EXACT_SIZE flag
Instead of setting this flag on the ones used for the zero-copy negociation,
it is set on the connection flags used for xprt->rcv_buf()
call. Fortunately, there is no real consequence. The only visible effect is
the chunk size that is written on 8 bytes for no reason.

This patch is related to issue #2598. It must be backported to 3.0.

(cherry picked from commit 7bff576ebb5e9cc8d081f0a7f7b9cf657e5a3d13)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
2024-06-10 14:48:23 +02:00
Christopher Faulet
e4e79659f8 BUG/MAJOR: mux-h1: Properly copy chunked input data during zero-copy nego
When data are transfered via zero-copy data forwarding, if some data were
already received, we try to immediately tranfer it during the negociation
step. If data are chunked and the chunk size is unknown, 10 bytes are reserved
to write the chunk size during the done step. However, when input data are
finally transferred, the offset is ignored. Data are copied into the output
buffer. But the first 10 bytes are then crushed by the chunk size. Thus the
chunk is truncated leading to a malformed message.

This patch should fix the issue #2598. It must be backported to 3.0.

(cherry picked from commit e8cc8a60be614c1cf978233b0b97771c9cc8fa20)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
2024-06-10 14:38:48 +02:00
William Manley
913b7b672f BUG/MEDIUM: stconn/mux-h1: Fix suspect change causing timeouts
This fixes an issue I've had where if a connection was idle for ~23s
it would get in a bad state.  I don't understand this code, so I'm
not sure exactly why it was failing.

I discovered this by bisecting to identify the commit that caused the
regression between 2.9 and 3.0.  The commit is
d2c3f8dde7: "MINOR: stconn/connection:
Move shut modes at the SE descriptor level" - a part of v3.0-dev8.
It seems to be an innocent renaming, so I looked through it and this
stood out as suspect:

    -        if (mode != CO_SHW_NORMAL)
    +        if (mode & SE_SHW_NORMAL)

It looks like the not went missing here, so this patch reverses that
condition.  It fixes my test.

I don't quite understand what this is doing or is for so I can't write
a regression test or decent commit message.  Hopefully someone else
will be able to pick this up from where I've left it.

[CF: This inverts the condition to perform clean shutdowns. This means no
     clean shutdown are performed when it should do. This patch must be
     backported to 3.0]

(cherry picked from commit 52eb6b23f81d8663ebc6b2fe5d9996916c05ed8f)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
2024-06-10 14:38:45 +02:00
Amaury Denoyelle
0a360df9de BUG/MINOR: quic: ensure Tx buf is always purged
quic_conn API for sending was recently refactored. The main objective
was to regroup the different functions present for both handshake and
application emission.

After this refactoring, an optimization was introduced to avoid calling
qc_send() if there was nothing new to emit. However, this prevent the Tx
buffer to be purged if previous sending was interrupted, until new
frames are finally available.

To fix this, simply remove the optimization. qc_send() is thus now
always called in quic_conn IO handlers.

The impact of this bug should be minimal as it happens only on sending
temporary error. However in this case, this could cause extra latency or
even a complete sending freeze in the worst scenario.

This must be backported up to 3.0.

(cherry picked from commit 0ef94e2dff873c0755584d0797c81e1b2c697e52)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
2024-06-10 14:38:41 +02:00
Amaury Denoyelle
8728d680e1 BUG/MINOR: quic: fix computed length of emitted STREAM frames
qc_build_frms() is responsible to encode multiple frames in a single
QUIC packet. It accounts for room left in the buffer packet for each
newly encded frame.

An incorrect computation was performed when encoding a STREAM frame in a
single packet. Frame length was accounted twice which would reduce in
excess the buffer packet room. This caused the remaining built frames to
be reduced with the resulting packet not able to fill the whole MTU.

The impact of this bug should be minimal. It is only present when
multiple frames are encoded in a single packet after a STREAM. However
in this case datagrams built are smaller than expecting, which is
suboptimal for bandwith.

This should be backported up to 2.6.

(cherry picked from commit 50470a5181b6105ad1914c0d4e67794a2f69c80a)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
2024-06-10 14:38:37 +02:00
William Lallemand
be979dd127 BUG/MEDIUM: ssl: bad auth selection with TLS1.2 and WolfSSL
The ClientHello callback for WolfSSL introduced in haproxy 2.9, seems
not to behave correctly with TLSv1.2.

In TLSv1.2, this is the cipher that is used to chose the authentication algorithm
(ECDSA or RSA), however an SSL client can send a signature algorithm.

In TLSv1.3, the authentication is not part of the ciphersuites, and
is selected using the signature algorithm.

The mistake in the code is that the signature algorithm in TLSv1.2 are
overwritting the auth that was selected using the ciphers.

This must be backported as far as 2.9.

(cherry picked from commit 711338e1ceb061db0a5c832acdea8edbeafa712f)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
2024-06-10 14:38:34 +02:00
William Lallemand
3738434b22 BUG/MEDIUM: ssl: wrong priority whem limiting ECDSA ciphers in ECDSA+RSA configuration
The ClientHello Callback which is used for certificate selection uses
both the signature algorithms and the ciphers sent by the client.

However, when a client is announcing both ECDSA and RSA capabilities
with ECSDA ciphers that are not available on haproxy side and RSA
ciphers that are compatibles, the ECDSA certificate will still be used
but this will result in a "no shared cipher" error, instead of a
fallback on the RSA certificate.

For example, a client could send
'ECDHE-ECDSA-AES128-CCM:ECDHE-RSA-AES256-SHA and HAProxy could be
configured with only 'ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA'.

This patch fixes the issue by validating that at least one ECDSA cipher
is available on both side before chosing the ECDSA certificate.

This must be backported on all stable versions.

(cherry picked from commit 93cc23a35561cd89b353143d20962dd86aa82a9c)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
2024-06-06 14:16:07 +02:00
Christopher Faulet
96aef291cb BUG/MEDIUM: mux-quic: Don't unblock zero-copy fwding if blocked during nego
The previous fix (792a645ec2 ["BUG/MEDIUM: mux-quic: Unblock zero-copy
forwarding if the txbuf can be released"]) introduced a regression. The
zero-copy data forwarding must only be unblocked if it was blocked by the
producer, after a successful negotiation.

It is important because during a negotiation, the consumer may be blocked
for another reason. Because of the flow control for instance. In that case,
there is not necessarily a TX buffer. And it unexpected to try to release an
unallocated TX buf.

In addition, the same may happen while a TX buf is still in-use. In that
case, it must also not be released. So testing the TX buffer is not the
right solution.

To fix the issue, a new IOBUF flag was added (IOBUF_FL_FF_WANT_ROOM). It
must be set by the producer if it is blocked after a sucessful negotiation
because it needs more room. In that case, we know a buffer was provided by
the consummer. In done_fastfwd() callback function, it is then possible to
safely unblock the zero-copy data forwarding if this flag is set.

This patch must be backported to 3.0 with the commit above.

(cherry picked from commit 9748df29ff655a9626d6e75ea9db79bb9afa2a50)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
2024-06-06 14:15:51 +02:00
Aurelien DARRAGON
f2384ec8fa CLEANUP: hlua: simplify ambiguous lua_insert() usage in hlua_ctx_resume()
'lua_insert(lua->T, -lua_gettop(lua->T))' is actually used to rotate the
top value with the bottom one, thus the code was overkill and the comment
was actually misleading, let's fix that by using explicit equivalent form
(absolute index).

It may be backported with 5508db9a2 ("BUG/MINOR: hlua: fix unsafe
lua_tostring() usage with empty stack") to all stable versions to ease
code maintenance.

(cherry picked from commit 2bde0d64ddf0e32257444f14e69adea8f899b74b)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
2024-06-06 14:15:39 +02:00
Aurelien DARRAGON
8b8fb7776e BUG/MINOR: hlua: fix leak in hlua_ckch_set() error path
in hlua_ckch_commit_yield() and hlua_ckch_set(), when an error occurs,
we enter the error path and try to raise an error from the <err> msg
pointer which must be freed afterwards.

However, the fact that luaL_error() never returns was overlooked, because
of that <err> msg is never freed in such case.

To fix the issue, let's use hlua_pushfstring_safe() helper to push the
err on the lua stack and then free it before throwing the error using
lua_error().

It should be backported up to 2.6 with 30fcca18 ("MINOR: ssl/lua:
CertCache.set() allows to update an SSL certificate file")

(cherry picked from commit 755c2daf0f88885fd6825c55ae59198726c4905e)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
2024-06-06 14:15:27 +02:00
Aurelien DARRAGON
342d239c7d BUG/MINOR: hlua: prevent LJMP in hlua_traceback()
Function is often used on error paths where no precaution is taken
against LJMP. Since the function is used on error paths (which include
out-of-memory error paths) the function lua_getinfo() could also raise
a memory exception, causing the process to crash or improper error
handling if the caller isn't prepared against that eventually. Since the
function is only used on rare events (error handling) and is lacking the
__LJMP prototype pefix, let's make it safe by protecting the lua_getinfo()
call so that hlua_traceback() callers may use it safely now (the function
will always succeed, output will be truncated in case of error).

This could be backported to all stable versions.

(cherry picked from commit 365ee28510a11993176ffd22704676732c051a4f)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
2024-06-06 14:15:11 +02:00
Aurelien DARRAGON
2219de9b56 BUG/MINOR: hlua: fix unsafe hlua_pusherror() usage
Following previous commit's logic: hlua_pusherror() is mainly used
from cleanup paths where the caller isn't protected against LJMPs.

Caller was tempted to think that the function was safe because func
prototype was lacking the __LJMP prefix.

Let's make the function really LJMP-safe by wrapping the sensitive calls
under lua_pcall().

This may be backported to all stable versions.

(cherry picked from commit f0e5b825cfba3ad710818e46c048ca296978283a)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
2024-06-06 14:15:01 +02:00
Aurelien DARRAGON
8e74276df7 BUG/MINOR: hlua: don't use lua_pushfstring() when we don't expect LJMP
lua_pushfstring() is used in multiple cleanup paths (upon error) to
push the error message that will be raised by lua_error(). However this
is often done from an unprotected environment, or in the middle of a
cleanup sequence, thus we don't want the function to LJMP! (it may cause
various issues ranging from memory leaks to crashing the process..)

Hopefully this has very few chances of happening but since the use of
lua_pushfstring() is limited to error reporting here, it's ok to use our
own hlua_pushfstring_safe() implementation with a little overhead to
ensure that the function will never LJMP.

This could be backported to all stable versions.

(cherry picked from commit c0a3c1281fac10ca7a590a4c34d102c8040e97a5)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
2024-06-06 14:14:48 +02:00
Aurelien DARRAGON
1780d58964 CLEANUP: hlua: use hlua_pusherror() where relevant
In hlua_map_new(), when error occurs we use a combination of luaL_where,
lua_pushfstring and lua_concat to build the error string before calling
lua_error().

It turns out that we already have the hlua_pusherror() macro which is
exactly made for that purpose so let's use it.

It could be backported to all stable versions to ease code maintenance.

(cherry picked from commit 6e484996c6e3e5d7fc35fef77333b4f64d514fcb)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
2024-06-06 14:14:37 +02:00
Amaury Denoyelle
30fb3caf99 BUG/MINOR: quic: prevent crash on qc_kill_conn()
Ensure idle_timer task is allocated in qc_kill_conn() before waking it
up. It can be NULL if idle timer has already fired but MUX layer is
still present, which prevents immediate quic_conn release.

qc_kill_conn() is only used on send() syscall fatal error to notify
upper layer of an error and close the whole connection asap.

This crash occurence is pretty rare as it relies on timing issues. It
happens only if idle timer occurs before the MUX release (a bigger
client timeout is thus required) and any send() syscall detected error.
For now, it was only reproduced using GDB to interrupt haproxy longer
than the idle timeout.

This should be backported up to 2.6.

(cherry picked from commit f7ae84e7d1b20201b38348d9dcbaefa47eb29814)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
2024-06-06 14:14:27 +02:00
Christopher Faulet
91d9c4367c BUG/MEDIUM: mux-quic: Unblock zero-copy forwarding if the txbuf can be released
In done_fastfwd() callback function, if nothing was forwarding while the SD
is blocked, it means there is not enough space in the buffer to proceed. It
may be because there are data to be sent. But it may also be data already
sent waiting for an ack. In this case, no data to be sent by the mux. So the
quic stream is not woken up when data are finally removed from the
buffer. The data forwarding can thus be stuck. This happens when the stats
page is requested in QUIC/H3. Only applets are affected by this issue and
only with the QUIC multiplexer because it is the only mux with already sent
data in the TX buf.

To fix the issue, the idea is to release the txbuf if possible and then
unblock the SD to perform a new zero-copy data forwarding attempt. Doing so,
and thanks to the previous patch ("MEDIUM: applet: Be able to unblock
zero-copy data forwarding from done_fastfwd"), the applet will be woken up.

This patch should fix the issue #2584. It must be backported to 3.0.

(cherry picked from commit 792a645ec21126c74c33820d1e0de63ee98aa810)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
2024-06-06 14:14:18 +02:00
Christopher Faulet
47c5f80e5e MEDIUM: stconn: Be able to unblock zero-copy data forwarding from done_fastfwd
This part is only experienced by applet. When an applet try to forward data
via an iobuf, it may decide to block for any reason even if there is free
space in the buffer. For instance, the stats applet don't procude data if
the buffer is almost full.

However, in this case, it could be good to let the consumer decide a new
attempt is possible because more space was made. So, if IOBUF_FL_FF_BLOCKED
flag is removed by the consumer when done_fastfwd() callback function is
called, the SE_FL_WANT_ROOM flag is removed on the producer sedesc. It is
only done for applets. And thanks to this change, the applet can be woken up
for a new attempt.

This patch is required for a fix on the QUIC multiplexer.

(cherry picked from commit d2a2014f152c09ff4f28c03a4ff6c5aae000ad5d)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
2024-06-06 14:14:11 +02:00
Christopher Faulet
1b8593e1b1 BUG/MEDIUM: h1-htx: Don't state interim responses are bodyless
Interim responses are by definition bodyless. But we must not set the
corresponding HTX start-line flag, beecause the start-line of the final
response is still expected. Setting the flag above too early may lead the
multiplexer on the sending side to consider the message is finished after
the headers of the interim message.

It happens with the H2 multiplexer on frontend side if a "100-Continue" is
received from the server. The interim response is sent and
HTX_SL_F_BODYLESS_RESP flag is evaluated. Then, the headers of the final
response are sent with ES flag, because HTX_SL_F_BODYLESS_RESP flag was seen
too early, leading to a protocol error if the response has a body.

Thanks to grembo for this analysis.

This patch should fix the issue #2587. It must be backported as far as 2.9.

(cherry picked from commit 7c84ee71f77616f569081060d81455c137fc13f5)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
2024-06-06 14:13:39 +02:00
Aurelien DARRAGON
fefdae8eb7 BUG/MINOR: hlua: use CertCache.set() from various hlua contexts
Using CertCache.set() from init context wasn't explicitly supported and
caused the process to crash:

crash.lua:
  core.register_init(function()
    CertCache.set{filename="reg-tests/ssl/set_cafile_client.pem", ocsp=""}
  end)

crash.conf:
  global
    lua-load crash.lua
  listen front
    bind localhost:9090 ssl crt reg-tests/ssl/set_cafile_client.pem ca-file reg-tests/ssl/set_cafile_interCA1.crt verify none

./haproxy -f crash.conf
[NOTICE]   (267993) : haproxy version is 3.0-dev2-640ff6-910
[NOTICE]   (267993) : path to executable is ./haproxy
[WARNING]  (267993) : config : missing timeouts for proxy 'front'.
   | While not properly invalid, you will certainly encounter various problems
   | with such a configuration. To fix this, please ensure that all following
   | timeouts are set to a non-zero value: 'client', 'connect', 'server'.
[1]    267993 segmentation fault (core dumped)  ./haproxy -f crash.conf

This is because in hlua_ckch_set/hlua_ckch_commit_yield, we always
consider that we're being called from a yield-capable runtime context.
As such, hlua_gethlua() is never checked for NULL and we systematically
try to wake hlua->task and yield every 10 instances.

In fact, if we're called from the body or init context (that is, during
haproxy startup), hlua_gethlua() will return NULL, and in this case we
shouldn't care about yielding because it is ok to commit all instances
at once since haproxy is still starting up.

Also, when calling CertCache.set() from a non-yield capable runtime
context (such as hlua fetch context), we kept doing as if the yield
succeeded, resulting in unexpected function termination (operation
would be aborted and the CertCache lock wouldn't be released). Instead,
now we explicitly state in the doc that CertCache.set() cannot be used
from a non-yield capable runtime context, and we raise a runtime error
if it is used that way.

These bugs were discovered by reading the code when trying to address
Svace report documented by @Bbulatov GH #2586.

It should be backported up to 2.6 with 30fcca18 ("MINOR: ssl/lua:
CertCache.set() allows to update an SSL certificate file")

(cherry picked from commit 4f906a9c3824dd424a36f53fc3479f276333d566)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
2024-06-06 14:13:09 +02:00
Willy Tarreau
43192a0af8 BUG/MINOR: tools: fix possible null-deref in env_expand() on out-of-memory
In GH issue #2586 @Bbulatov reported a theoretical null-deref in
env_expand() in case there's no memory anymore to expand an environment
variable. The function should return NULL in this case so that the only
caller (str2sa_range) sees it. In practice it may only happen during
boot thus is harmless but better fix it since it's easy. This can be
backported to all versions where this applies.

(cherry picked from commit ba958fb230d4add678913f18eb520d9d5935c968)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
2024-06-06 14:10:45 +02:00
Willy Tarreau
995bba75fd BUG/MINOR: tcpcheck: report correct error in tcp-check rule parser
When parsing tcp-check expect-header, a copy-paste error in the error
message causes the name of the header to be reporetd as the invalid
format string instead of its value. This is really harmless but should
be backported to all versions to help users understand the cause of the
problem when this happens. This was reported in GH issue #2586 by
@Bbulatov.

(cherry picked from commit 8a7afb6964b6f57c8d0481e2e391b7ec88552b26)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
2024-06-06 14:07:11 +02:00
Willy Tarreau
6d0ac5d07a BUG/MINOR: cfgparse: remove the correct option on httpcheck send-state warning
In GH issue #2586 @Bbulatov reported a bug where the http-check
send-state flag is removed from options instead of options2 when
http-check is disabled. It only has an effect when this option is
set and http-check disabled, where it displays a warning indicating
this will be ignored. The option removed instead is srvtcpka when
this happens. It's likely that both options being so minor, nobody
ever faced it.

This can be backported to all versions.

(cherry picked from commit d8194fab8205364d65cf5364e6daeca517eb75e0)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
2024-06-06 14:06:52 +02:00
Valentine Krasnobaeva
d5e43caaf5 BUG/MINOR: activity: fix Delta_calls and Delta_bytes count
Thanks to the commit 5714aff4a6
"DEBUG: pool: store the memprof bin on alloc() and update it on free()", the
amount of memory allocations and memory "frees" is shown now on the same line,
corresponded to the caller name. This is very convenient to debug memory leaks
(haproxy should run with -dMcaller option).

The implicit drawback of this solution is that we count twice same free_calls
and same free_tot (bytes) values in cli_io_handler_show_profiling(), when
we've calculed tot_free_calls and tot_free_bytes, by adding them to the these
totalizators for p_alloc, malloc and calloc allocator types. See the details
about why this happens in a such way in __pool_free() implementation and
also in the commit message for 5714aff4a6.

This double addition of free counters falses 'Delta_calls' and 'Delta_bytes',
sometimes we even noticed that they show negative values.

Same problem was with the calculation of average allocated buffer size for
lines, where we show simultaneously the number of allocated and freed bytes.
2024-05-28 19:25:08 +02:00
Willy Tarreau
decb7c90df CLEANUP: ssl_sock: move dirty openssl-1.0.2 wrapper to openssl-compat
Valentine noticed this ugly SSL_CTX_get_tlsext_status_cb() macro
definition inside ssl_sock.c that is dedicated to openssl-1.0.2 only.
It would be better placed in openssl-compat.h, which is what this
patch does. It also addresses a missing pair of parenthesis and
removes an invalid extra semicolon.
2024-05-28 19:17:57 +02:00
Valentine Krasnobaeva
84380965a5 BUG/MINOR: ssl/ocsp: init callback func ptr as NULL
In ssl_sock_load_ocsp() it is better to initialize local scope variable
'callback' function pointer as NULL, while we are declaring it. According to
SSL_CTX_get_tlsext_status_cb() API, then we will provide a pointer to this
'on stack' variable in order to check, if the callback was already set before:

OpenSSL 1.x.x and 3.x.x:
  long SSL_CTX_get_tlsext_status_cb(SSL_CTX *ctx, int (**callback)(SSL *, void *));
  long SSL_CTX_set_tlsext_status_cb(SSL_CTX *ctx, int (*callback)(SSL *, void *));

WolfSSL 5.7.0:
  typedef int(*tlsextStatusCb)(WOLFSSL* ssl, void*);
  WOLFSSL_API int wolfSSL_CTX_get_tlsext_status_cb(WOLFSSL_CTX* ctx, tlsextStatusCb* cb);
  WOLFSSL_API int wolfSSL_CTX_set_tlsext_status_cb(WOLFSSL_CTX* ctx, tlsextStatusCb cb);

When this func ptr variable stays uninitialized, haproxy comipled with ASAN
crushes in ssl_sock_load_ocsp():

  ./haproxy -d -f haproxy.cfg
  ...
  AddressSanitizer:DEADLYSIGNAL
  =================================================================
  ==114919==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x5eab8951bb32 bp 0x7ffcdd6d8410 sp 0x7ffcdd6d82e0 T0)
  ==114919==The signal is caused by a READ memory access.
  ==114919==Hint: address points to the zero page.
    #0 0x5eab8951bb32 in ssl_sock_load_ocsp /home/vk/projects/haproxy/src/ssl_sock.c:1248:22
    #1 0x5eab89510d65 in ssl_sock_put_ckch_into_ctx /home/vk/projects/haproxy/src/ssl_sock.c:3389:6
  ...

This happens, because callback variable is allocated on the stack. As not
being explicitly initialized, it may contain some garbage value at runtime,
due to the linked crypto library update or recompilation.

So, following ssl_sock_load_ocsp code, SSL_CTX_get_tlsext_status_cb() may
fail, callback will still contain its initial garbage value,
'if (!callback) {...' test will put us on the wrong path to access some
ocsp_cbk_arg properties via its pointer, which won't be set and like this
we will finish with segmentation fault.

Must be backported in all stable versions. All versions does not have
the ifdef, the previous cleanup patch is useful starting from the 2.7
version.
2024-05-28 18:14:26 +02:00
Valentine Krasnobaeva
fb7b46d267 CLEANUP: ssl/ocsp: readable ifdef in ssl_sock_load_ocsp
Due to the support of different TLS/SSL libraries and its different versions,
sometimes we are forced to use different internal typedefs and callback
functions. We strive to avoid this, but time to time "#ifdef... #endif"
become inevitable.

In particular, in ssl_sock_load_ocsp() we define a 'callback' variable, which
will contain a function pointer to our OCSP stapling callback, assigned
further via SSL_CTX_set_tlsext_status_cb() to the intenal SSL context
struct in a linked crypto library.

If this linked crypto library is OpenSSL 1.x.x/3.x.x, for setting and
getting this callback we have the following API signatures
(see doc/man3/SSL_CTX_set_tlsext_status_cb.pod):

  long SSL_CTX_get_tlsext_status_cb(SSL_CTX *ctx, int (**callback)(SSL *, void *));
  long SSL_CTX_set_tlsext_status_cb(SSL_CTX *ctx, int (*callback)(SSL *, void *));

If we are using WolfSSL, same APIs expect tlsextStatusCb function prototype,
provided via the typedef below (see wolfssl/wolfssl/ssl.h):

  typedef int(*tlsextStatusCb)(WOLFSSL* ssl, void*);
  WOLFSSL_API int wolfSSL_CTX_get_tlsext_status_cb(WOLFSSL_CTX* ctx, tlsextStatusCb* cb);
  WOLFSSL_API int wolfSSL_CTX_set_tlsext_status_cb(WOLFSSL_CTX* ctx, tlsextStatusCb cb);

It seems, that in OpenSSL < 1.0.0, there was no support for OCSP extention, so
no need to set this callback.

Let's avoid #ifndef... #endif for this 'callback' variable definition to keep
things clear. #ifndef... #endif are usually less readable, than
straightforward "#ifdef... #endif".
2024-05-28 18:00:44 +02:00
Willy Tarreau
725fa0ecd2 BUILD: fd: errno is also needed without poll()
When building without USE_POLL, fd.c fails on errno because that one is
only included when USE_POLL is set. Let's move it outside of the ifdef.
2024-05-27 19:14:14 +02:00
Aurelien DARRAGON
435a9da267 MINOR: log: rename 'log-format tag' to 'log-format alias'
In 2.9 we started to introduce an ambiguity in the documentation by
referring to historical log-format variables ('%var') as log-format
tags in 739c4e5b1e ("MINOR: sample: accept_date / request_date return
%Ts / %tr timestamp values") and 454c372b60 ("DOC: configuration: add
sample fetches for timing events").

In fact, we've had this confusion between log-format tag and log-format
var for more than 10 years now, but in 2.9 it was the first time the
confusion was exposed in the documentation.

Indeed, both 'log-format variable' and 'log-format tag' actually refer
to the same feature (that is: '%B' and friends that can be used for
direct access to some log-oriented predefined fetches instead of using
%[expr] with generic sample expressions).

This feature was first implemented in 723b73ad75 ("MINOR: config: Parse
the string of the log-format config keyword") and later documented in
4894040fa ("DOC: log-format documentation"). At that time, it was clear
that we used to name it 'log-format variable'.

But later the same year, 'log-format tag' naming started to appear in
some commit messages (while still referring to the same feature), for
instance with ffc3fcd6d ("MEDIUM: log: report SSL ciphers and version
in logs using logformat %sslc/%sslv").

Unfortunately in 2.9 when we added (and documented) new log-format
variables we officially started drifting to the misleading 'log-format
tag' naming (perhaps because it was the most recent naming found for
this feature in git log history, or because the confusion has always
been there)

Even worse, in 3.0 this confusion led us to rename all 'var' occurrences
to 'tag' in log-format related code to unify the code with the doc.

Hopefully William quickly noticed that we made a mistake there, but
instead of reverting to historical naming (log-format variable), it was
decided that we must use a different name that is less confusing than
'tags' or 'variables' (tags and variables are keywords that are already
used to designate other features in the code and that are not very
explicit under log-format context today).

Now we refer to '%B' and friends as a logformat alias, which is
essentially a handy way to print some log oriented information in the
log string instead of leveraging '%[expr]' with generic sample expressions
made of fetches and converters. Of course, there are some subtelties, such
as a few log-format aliases that still don't have sample fetch equivalent
for historical reasons, and some aliases that may be a little faster than
their generic sample expression equivalents because most aliases are
pretty much hardcoded in the log building function. But in general
logformat aliases should be simply considered as an alternative to using
expressions (with '%[expr']')

Also, under log-format context, when we want to refer to either an alias
('%alias') or an expression ('%[expr]'), we should use the generic term
'logformat item', which in fact designates a single item within the
logformat string provided by the user. Indeed, a logformat item (whether
is is an alias or an expression) always starts with '%' and may accept
optional flags / arguments

Both the code and the documentation were updated in that sense, hopefully
this will clarify things and prevent future confusions.
2024-05-27 17:03:48 +02:00
William Lallemand
0a00302fab MINOR: sample: implement the uptime sample fetch
'uptime' returns the uptime of the current HAProxy worker in seconds.
2024-05-27 11:06:40 +02:00
Christopher Faulet
0d7c1bc6ab BUG/MINOR: server: Don't reset resolver options on a new default-server line
When a new "default-server" line is parsed, some resolver options are reset.
Thus previously defined default options cannot be inherited. There is no
reason to do so. First because other server options are inherited. And then
because not all resolver options are reset. It is not consistent.

This patch should fix issue #2559. It should be backported to all stable
versions.
2024-05-24 16:31:01 +02:00
Christopher Faulet
8d2514e087 BUG/MINOR: http-htx: Support default path during scheme based normalization
As stated in RFC3986, for an absolute-form URI, an empty path should be
normalized to a path of "/". This is part of scheme based normalization
rules. This kind of normalization is already performed for default ports. So
we might as well deal with the case of empty path.

The associated reg-tests was updated accordingly.

This patch should fix the issue #2573. It may be backported as far as 2.4 if
necessary.
2024-05-24 16:17:24 +02:00
Aurelien DARRAGON
c16eba8183 BUG/MEDIUM: server/dns: preserve server's port upon resolution timeout or error
@boi4 reported in GH #2578 that since 3.0-dev1 for servers with address
learned from A/AAAA records after a DNS flap server would be put out of
maintenance with proper address but with invalid port (== 0), making it
unusable and causing tcp checks to fail:

[NOTICE]   (1) : Loading success.
[WARNING]  (8) : Server mybackend/myserver1 is going DOWN for maintenance (DNS refused status). 0 active and 0 backup servers left. 0 sessions active, 0 requeued, 0 remaining in queue.
[ALERT]    (8) : backend 'mybackend' has no server available!
[WARNING]  (8) : mybackend/myserver1: IP changed from '(none)' to '127.0.0.1' by 'myresolver/ns1'.
[WARNING]  (8) : Server mybackend/myserver1 ('myhost') is UP/READY (resolves again).
[WARNING]  (8) : Server mybackend/myserver1 administratively READY thanks to valid DNS answer.
[WARNING]  (8) : Server mybackend/myserver1 is DOWN, reason: Layer4 connection problem, info: "Connection refused", check duration: 0ms. 0 active and 0 backup servers left. 0 sessions active, 0 requeued, 0 remaining in queue.

@boi4 also mentioned that this used to work fine before.

Willy suggested that this regression may have been introduced by 64c9c8e
("BUG/MINOR: server/dns: use server_set_inetaddr() to unset srv addr from DNS")

Turns out he was right! Indeed, in 64c9c8e we systematically memset the
whole server_inetaddr struct (which contains both the requested server's
addr and port planned for atomic update) instead of only memsetting the
addr part of the structure: except when SRV records are involved (SRV
records provide both the address and the port unlike A or AAAA records),
we must not reset the server's port upon DNS errors because the port may
have been provided at config time and we don't want to lose its value.

Big thanks to @boi4 for his well-documented issue that really helped us to
pinpoint the bug right on time for the dev-13 release.

No backport needed (unless 64c9c8e gets backported).
2024-05-24 15:29:48 +02:00
Amaury Denoyelle
98ed11b0c5 BUG/MINOR: rhttp: initialize session origin after preconnect reversal
Since the following commit, session is initialized early for rhttp
preconnect.

  12c40c25a9
  MEDIUM: rhttp: create session for active preconnect

Session origin member was not set. However, this prevents several
session fetches to not work as expected. Worst, this caused a regression
as previously session was created after reversal with origin member
defined. This was reported by user William Manley on the mailing-list
which rely on set-dst.

One possible fix would be to set origin on session_new(). However, as
this is done before reversal, some session members may be incorrectly
initialized, in particular source and destination address.

Thus, session origin is only set after reversal is completed. This
ensures that session fetches have the same behavior on standard
connections and reversable ones.

This does not need to be backported.
2024-05-24 14:47:21 +02:00
Amaury Denoyelle
47168e217a MEDIUM: connection: use pool-conn-name instead of sni on reuse
Implement pool-conn-name support for idle connection reuse. It replaces
SNI as arbitrary identifier for connections in the idle pool. Thus,
every SNI reference in this context have been replaced.

Main change occurs in connect_server() where pool-conn-name sample fetch
is now prehash to generate idle connection identifier. SNI is now solely
used in the context of SSL for ssl_sock_set_servername().
2024-05-24 14:47:21 +02:00
Amaury Denoyelle
be4f89f2b2 MINOR: server: define pool-conn-name keyword
Define a new server keyword pool-conn-name. The purpose of this keyword
will be to identify connections inside the idle connections pool,
replacing SNI in case SSL is not wanted.

This keyword uses a sample expression argument. It thus can reuse
existing function parse_srv_expr() for parsing. In the future, it may be
necessary to define a keyword variant which uses a logformat for
extensability.

This patch only implement parsing. Argument is stored inside new server
field <pool_conn_name> and expression is generated in
_srv_parse_finalize() into <pool_conn_name_expr>.

If pool-conn-name is not set but SNI is, the latter is reused
automatically as pool-conn-name via _srv_parse_finalize(). This ensures
current reuse behavior remains compatible and idle connection reuse will
not mix connections with different SNIs by mistake.

Main usage will be for rhttp when SSL is not wanted between the two
haproxy instances. Previously, it was possible to use "sni" keyword even
without SSL on a server line which have a similar effect. However,
having a dedicated "pool-conn-name" keyword is deemed clearer. Besides,
it would allow for more complex configuration where pool-conn-name and
SNI are use in parallel with different values.
2024-05-24 14:36:31 +02:00
Amaury Denoyelle
91001422b4 MINOR: server: generalize sni expr parsing
Two functions exists for server sni sample expression parsing. This is
confusing so this commit aims at clarifying this.

Functions are renamed with the following identifiers. First function is
named parse_srv_expr() and can be used during parsing. Besides
expression parsing, it has ensure sample fetch validity in the context
of a server line.

Second function is renamed _parse_srv_expr() and is used internally by
parse_srv_expr(). It only implements sample parsing without extra
checks. It is already use for server instantiation derived from
server-template as checks were already performed. Also, it is now used
in http-client code as SNI is a fixed string.

Finally, both functions are generalized to remove any reference to SNI.
This will allow to reuse it to parse other server keywords which use an
expression. This will be the case for the future keyword pool-conn-name.
2024-05-24 14:36:31 +02:00
Amaury Denoyelle
b9f67a46a2 MINOR: quic: clarify doc for quic_recv()
Just highlight the fact that quic_recv() only receive a single datagram.
2024-05-24 14:36:31 +02:00
Amaury Denoyelle
5764bc50b5 BUG/MINOR: quic: adjust restriction for stateless reset emission
Review RFC 9000 and ensure restriction on Stateless reset are properly
enforced. After careful examination, several changes are introduced.

First, redefine minimal Stateless Reset emitted packet length to 21
bytes (5 random bytes + a token). This is the new default length used in
every case, unless received packet which triggered it is 43 bytes or
smaller.

Ensure every Stateless Reset packets emitted are at 1 byte shorter than
the received packet which triggered it. No Stateless reset will be
emitted if this falls under the above limit of 21 bytes. Thus this
should prevent looping issues.

This should be backported up to 2.6.
2024-05-24 14:36:31 +02:00
Amaury Denoyelle
f55748a422 MAJOR: config: prevent QUIC with clients privileged port by default
Previous commit introduce new protection mechanism to forbid
communications with clients which use a privileged source port. By
default, this mechanism is disabled for every protocols.

This patch changes the default value and activate the protection
mechanism for QUIC protocol. This is justified as it is a probable sign
of DNS/NTP amplification attack.

This is labelled as major as it can be a breaking change with some
network environments.
2024-05-24 14:36:31 +02:00
Amaury Denoyelle
45f40bac4c MEDIUM: config: prevent communication with privileged ports
This commit introduces a new global setting named
harden.reject_privileged_ports.{tcp|quic}. When active, communications
with clients which use privileged source ports are forbidden. Such
behavior is considered suspicious as it can be used as spoofing or
DNS/NTP amplication attack.

Value is configured per transport protocol. For each TCP and QUIC
distinct code locations are impacted by this setting. The first one is
in sock_accept_conn() which acts as a filter for all TCP based
communications just after accept() returns a new connection. The second
one is dedicated for QUIC communication in quic_recv(). In both cases,
if a privileged source port is used and setting is disabled, received
message is silently dropped.

By default, protection are disabled for both protocols. This is to be
able to backport it without breaking changes on stable release.

This should be backported as it is an interesting security feature yet
relatively simple to implement.
2024-05-24 14:36:31 +02:00
Amaury Denoyelle
4e632545f7 BUILD: trace: fix warning on null dereference
Since a recent change on trace, the following compilation warning may
occur :
  src/trace.c: In function ‘trace_parse_cmd’:
  src/trace.c:865:33: error: potential null pointer dereference [-Werror=null-dereference]
    865 |                         for (nd = src->decoding; nd->name && nd->desc; nd++)
        |                              ~~~^~~~~~~~~~~~~~~

Fix this by rearranging code path to better highlight that only "quiet"
verbosity is allowed if no trace source is specified.

This was detected with GCC 14.1.
2024-05-24 14:36:03 +02:00
Aurelien DARRAGON
c9af6d5414 DEBUG: pollers/fd: add thread id suffix to per-thread memory areas name hints
Willy reported that since abb8412d2 ("DEBUG: pollers: add name hint for
large memory areas used by pollers") and 22ec2ad8b ("DEBUG: fd: add name
hint for large memory areas") multiple maps with the same name could be
found in /proc/<pid>/maps when haproxy process is started with multiple
threads, which can be annoying.

In fact this happens because some poller and fd-created memory areas are
being created for each available thread, and since the naming was done
using vma_set_name() with the same <type> and <name> inputs, the resulting
name was the same for all threads.

Thanks to the previous commit, we now use vma_set_name_id() for naming
per-thread memory areas so that "-id" prefix is appended after the name
name, where "id" equals to 'tid+1' (to match the thread numbering logic
found in config file or in ha_panic() report),  allowing to easily
identify which haproxy thread owns the map in /proc/<pid>/maps:

7d3b26200000-7d3b26a01000 rw-p 00000000 00:00 0                          [anon:ev_poll:poll_events-2]
7d3b26c00000-7d3b27001000 rw-p 00000000 00:00 0                          [anon:fd:fd_updt-2]
7d3b27200000-7d3b27a01000 rw-p 00000000 00:00 0                          [anon:ev_poll:poll_events-1]
7d3b34200000-7d3b34601000 rw-p 00000000 00:00 0                          [anon:fd:fd_updt-1]
2024-05-24 12:07:18 +02:00
Aurelien DARRAGON
9d37c4b989 DEBUG: tools: add vma_set_name_id() helper
Just like vma_set_name() from 51a8f134e ("DEBUG: tools: add vma_set_name()
helper"), but also takes <id> as parameter to append "-$id" suffix after
the name in order to differentiate 2 areas that were named using the same
<type> and <name> combination.

example, using mmap + MAP_SHARED|MAP_ANONYMOUS:
  7364c4fff000-736508000000 rw-s 00000000 00:01 3540  [anon_shmem:type:name-id]
Another example, using mmap + MAP_PRIVATE|MAP_ANONYMOUS or using
glibc/malloc() above MMAP_THRESHOLD:
  7364c4fff000-736508000000 rw-s 00000000 00:01 3540  [anon:type:name-id]
2024-05-24 12:07:13 +02:00
Aurelien DARRAGON
23814a44e5 CLEANUP: tools: fix vma_set_name() function comment
There was a typo in the example provided in vma_set_name(): maps named
using the function will show up as "type:name", not "type.name", updating
the comment to reflect the current behavior.
2024-05-24 12:07:07 +02:00
Willy Tarreau
0bda33a3ec MINOR: stick-tables: remove the uneeded read lock in stksess_free()
During changes made in 2.7 by commits 8d3c3336f9 ("MEDIUM: stick-table:
make stksess_kill_if_expired() avoid the exclusive lock") and 996f1a5124
("MEDIUM: stick-table: do not take a lock to update t->current anymore."),
the operation was done cautiously one baby step at a time and the final
cleanup was not done, as we're keeping a read lock under an atomic dec.
Furthermore there's a pool_free() call under that lock, and we try to
avoid pool_alloc() and pool_free() under locks for their nasty side
effects (e.g. when memory gets recompacted), so let's really drop it
now.

Note that the performance gain is not really perceptible here, it's
essentially for code clarity reasons that this has to be done.
2024-05-24 11:52:57 +02:00
Willy Tarreau
8580f9db20 CLEANUP: stick-tables: remove a few unneeded tests for use_wrlock
Due to the code in stktable_touch_with_exp() being the same as in other
functions previously made around a loop trying first to upgrade a read
lock then to fall back to a direct write lock, there remains a confusing
construct with multiple tests on use_wrlock that is obviously zero when
tested. Let's remove them since the value is known and the loop does not
exist anymore.
2024-05-24 11:52:19 +02:00
Willy Tarreau
77f286e8bc BUG/MEDIUM: stick-tables: make sure never to create two same remote entries
In GH issue #2552, Christian Ruppert reported an increase in crashes
with recent 3.0-dev versions, always related with stick-tables and peers.
One particularity of his config is that it has a lot of peers.

While trying to reproduce, it empirically was found that firing 10 load
generators at 10 different haproxy instances tracking a random key among
100k against a table of max 5k entries, on 8 threads and between a total
of 50 parallel peers managed to reproduce the crashes in seconds, very
often in ebtree deletion or insertion code, but not only.

The debugging revealed that the crashes are often caused by a parent node
being corrupted while delete/insert tries to update it regarding a recently
inserted/removed node, and that that corrupted node had always been proven
to be deleted, then immediately freed, so it ought not be visited in the
tree from functions enclosed between a pair of lock/unlock. As such the
only possibility was that it had experienced unexpected inserts. Also,
running with pool integrity checking would 90% of the time cause crashes
during allocation based on corrupted contents in the node, likely because
it was found at two places in the same tree and still present as a parent
of a node being deleted or inserted (hence the __stksess_free and
stktable_trash_oldest callers being visible on these items).

Indeed the issue is in fact related to the test set (occasionally redundant
keys, many peers). What happens is that sometimes, a same key is learned
from two different peers. When it is learned for the first time, we end up
in stktable_touch_with_exp() in the "else" branch, where the test for
existence is made before taking the lock (since commit cfeca3a3a3
("MEDIUM: stick-table: touch updates under an upgradable read lock") that
was merged in 2.9), and from there the entry is added. But is one of the
threads manages to insert it before the other thread takes the lock, then
the second thread will try to insert this node again. And inserting an
already inserted node will corrupt the tree (note that we never switched
to enforcing a check in insertion code on this due to API history that
would break various code parts).

Here the solution is simple, it requires to recheck leaf_p after getting
the lock, to avoid touching anything if the entry has already been
inserted in the mean time.

Many thanks to Christian Ruppert for testing this and for his invaluable
help on this hard-to-trigger issue.

This fix needs to be backported to 2.9.
2024-05-24 11:52:11 +02:00
Christopher Faulet
9938fb9c7a BUG/MEDIUM: stick-tables: Fix race with peers when killing a sticky session
When a sticky session is killed, we must be sure no other entity is still
referencing it. The session's ref_cnt must be 0. However, there is a race
with peers, as decribed in 21447b1dd4 ("BUG/MAJOR: stick-tables: fix race
with peers in entry expiration"). When the update lock is acquire, we must
recheck the ref_cnt value.

This patch is part of a debugging session about issue #2552. It must be
backported to 2.9.
2024-05-24 11:52:11 +02:00
Christopher Faulet
dfd938bad6 BUG/MEDIUM: stick-tables: Fix race with peers when trashing oldest entries
It is the same that the one fixed in process_table_expire() (21447b1dd4
["BUG/MAJOR: stick-tables: fix race with peers in entry expiration"]). In
stktable_trash_oldest(), when the update lock is acquired, we must take care
to check again the ref_cnt because some peers may increment it (See commit
above for details).

This patch fixes a crash mentionned in 2552#issuecomment-2110532706. It must
be backported to 2.9.
2024-05-24 11:52:11 +02:00
Willy Tarreau
51f9f6cfd4 BUILD: quic: fix unused variable warning when threads are disabled
The tree variable was introduced in 3.0 by commit dd58dff1e6
("BUG/MEDIUM: quic: QUIC CID removed from tree without locking") which
was marked for backport. The variable is only used for locks.
Let's just mark the variable __maybe_unused for when the code is
built without threads.

The patch above was marked for backport to 2.7 so this should be
backported wherever the fix was backported.
2024-05-24 11:51:41 +02:00
Willy Tarreau
381ed2a4dd MINOR: config: add thread-hard-limit to set an upper bound to nbthread
On todays large systems, it's not always desired to run on all threads
for light loads, and usually users enforce nbthread to a lower value
(e.g. 8). The problem is that this is a fixed value, and moving such
configs to smaller machines continues to enforce the value and this
becomes extremely unproductive due to having more threads than CPUs.
This also happens quite a bit in VMs, containers, or cloud instances
of various sizes.

This commit introduces the thread-hard-limit setting that allows to only
set an upper bound to the number of threads without raising a lower value.
This means that using "thread-hard-limit 8" will make sure that no more
than 8 threads will be used when available, but it will remain two when
run on a dual-core machine.
2024-05-24 09:46:49 +02:00
Christopher Faulet
d11249f292 MINOR: mux-quic: Set abort info for SC-less QCS on STOP_SENDING frame
It is a revert of cc9827bb09 ("BUG/MEDIUM: mux-quic: fix crash on
STOP_SENDING received without SD"). This fix was based on a wrong assumption
about QUIC streams that may have no stream-endpoint descriptor. However, it
must never happen. And this was fixed. So we can now safely revert the
commit above. However, it is not a bugfix because, for now, abort info are
only used by the upper layer. So it is not a big deal to not set it when
there is no SC.
2024-05-23 11:18:19 +02:00
Christopher Faulet
086e51017e BUG/MEDIUM: mux-quic: Create sedesc in same time of the QUIC stream
Recent changes to save abort reason revealed an issue during the QUIC stream
creation. Indeed, by design, when a mux stream is created, it must always
have a valid stream-endpoint descriptor and it must remain valid till the
mux stream destruction. On frontend side, it is the multiplexer
responsibility to create it and set it as orphan. On the backend side, the
sedesc is provided by the upper layer. It is the sedesc of the back
stream-connector.

For the QUIC multiplexer, the stream-endpoint descriptor was only created
when the stream-connector was created and attached on it. It is unexpected
and some bugs may be introduced because there is no valid sedesc on a QUIC
stream. And a recent bug was introduced for this reason.

This patch must be backported as far as 2.6.
2024-05-23 11:18:06 +02:00
Frederic Lecaille
169fc0b771 BUG/MAJOR: quic: Crash with TLS_AES_128_CCM_SHA256 (libressl only)
At least 3.9.0 version of libressl TLS stack does not behave as others stacks like quictls which
make SSL_do_handshake() return an error when no cipher could be negotiated
in addition to emit a TLS alert(0x28). This is the case when TLS_AES_128_CCM_SHA256
is forced as TLS1.3 cipher from the client side. This make haproxy enter a code
path which leads to a crash as follows:

[Switching to Thread 0x7ffff76b9640 (LWP 23902)]
0x0000000000487627 in quic_tls_key_update (qc=qc@entry=0x7ffff00371f0) at src/quic_tls.c:910
910             struct quic_kp_trace kp_trace = {
(gdb) list
905     {
906             struct quic_tls_ctx *tls_ctx = &qc->ael->tls_ctx;
907             struct quic_tls_secrets *rx = &tls_ctx->rx;
908             struct quic_tls_secrets *tx = &tls_ctx->tx;
909             /* Used only for the traces */
910             struct quic_kp_trace kp_trace = {
911                     .rx_sec = rx->secret,
912                     .rx_seclen = rx->secretlen,
913                     .tx_sec = tx->secret,
914                     .tx_seclen = tx->secretlen,
(gdb) p qc
$1 = (struct quic_conn *) 0x7ffff00371f0
(gdb) p qc->ael
$2 = (struct quic_enc_level *) 0x0
(gdb) bt
 #0  0x0000000000487627 in quic_tls_key_update (qc=qc@entry=0x7ffff00371f0) at src/quic_tls.c:910
 #1  0x000000000049bca9 in qc_ssl_provide_quic_data (len=268, data=<optimized out>, ctx=0x7ffff0047f80, level=<optimized out>, ncbuf=<optimized out>) at src/quic_ssl.c:617
 #2  qc_ssl_provide_all_quic_data (qc=qc@entry=0x7ffff00371f0, ctx=0x7ffff0047f80) at src/quic_ssl.c:688
 #3  0x00000000004683a7 in quic_conn_io_cb (t=0x7ffff0047f30, context=0x7ffff00371f0, state=<optimized out>) at src/quic_conn.c:760
 #4  0x000000000063cd9c in run_tasks_from_lists (budgets=budgets@entry=0x7ffff76961f0) at src/task.c:596
 #5  0x000000000063d934 in process_runnable_tasks () at src/task.c:876
 #6  0x0000000000600508 in run_poll_loop () at src/haproxy.c:3073
 #7  0x0000000000600b67 in run_thread_poll_loop (data=<optimized out>) at src/haproxy.c:3287
 #8  0x00007ffff7f6ae45 in start_thread () from /lib64/libpthread.so.0
 #9  0x00007ffff78254af in clone () from /lib64/libc.so.6

When a TLS alert is emitted, haproxy calls quic_set_connection_close() which sets
QUIC_FL_CONN_IMMEDIATE_CLOSE connection flag. This is this flag which is tested
by this patch to make the handshake fail even if SSL_do_handshake() does not
return an error. This test is specific to libressl and never run with
others TLS stack.

Thank you to @lgv5 and @botovq for having reported this issue in GH #2569.

Must be backported as far as 2.6.
2024-05-22 15:21:55 +02:00
Valentine Krasnobaeva
0e93549d2a MINOR: proto: fix coding style
Remove redundant brackets for 'if' statements that contain only one
instruction.
2024-05-22 12:00:11 +02:00
Valentine Krasnobaeva
83ab1479d0 BUG/MINOR: sock: fix sock_create_server_socket
Set stream_err value as SF_ERR_NONE, if obtained socket fd has passed all
common runtime and configuration related checks.

'.connect()' method implementation in higher protocol layers requires Stream
Error Flag as the return value. So, at the socket layer, we need to pass to
sock_create_server_socket() a variable to set this flag, because syscalls and
some socket options checks are convenient to performe at the socket layer.
2024-05-22 11:59:55 +02:00
Willy Tarreau
5b9503ed33 MINOR: traces: enumerate the list of levels/verbosities when not found
It's quite frustrating, particularly on the command line, not to have
access to the list of available levels and verbosities when one does
not exist for a given source, because there's no easy way to find them
except by starting without and connecting to the CLI. Let's enumerate
the list of supported levels and verbosities when a name does not match.

For example:

  $ ./haproxy -db -f quic-repro.cfg -dt h2:help
  [NOTICE]   (9602) : haproxy version is 3.0-dev12-60496e-27
  [NOTICE]   (9602) : path to executable is ./haproxy
  [ALERT]    (9602) : -dt: no such trace level 'help', available levels are 'error', 'user', 'proto', 'state', 'data', and 'developer'.

  $ ./haproxy -db -f quic-repro.cfg -dt h2:user:help
  [NOTICE]   (9604) : haproxy version is 3.0-dev12-60496e-27
  [NOTICE]   (9604) : path to executable is ./haproxy
  [ALERT]    (9604) : -dt: no such trace verbosity 'help' for source 'h2', available verbosities for this source are: 'quiet', 'clean', 'minimal', 'simple', 'advanced', and 'complete'.

The same is done for the CLI where the existing help message is always
displayed when entering an invalid verbosity or level.
2024-05-22 11:17:57 +02:00
Amaury Denoyelle
60496e884e MINOR: connection: support PROXY v2 TLV emission without stream
Update API for PROXY protocol header encoding. Previously, it requires
stream parameter to be set. Change make_proxy_line() and associated
functions to add an extra session parameter. This is useful in context
where no stream is instantiated. For example, this is the case for rhttp
preconnect.

This change allows to extend PROXY v2 TLV encoding. Replace
build_logline() which requires a stream instance and call directly
sess_build_logline().

Note that stream parameter is kept as it is necessary for unique ID
encoding.

This change has no functional change for standard connections. However,
it is necessary to support TLV encoding on rhttp preconnect.
2024-05-22 10:01:57 +02:00
Amaury Denoyelle
7a81bfc8d2 MINOR: rhttp: support PROXY emission on preconnect
Extend preconnect to support PROXY protocol emission. Code is duplicated
from connect_server() into new_reverse_conn(). This is necessary to
support send-proxy on server line used as rhttp.
2024-05-22 10:01:57 +02:00
Amaury Denoyelle
12c40c25a9 MEDIUM: rhttp: create session for active preconnect
Modify rhttp preconnect by instantiating a new session for each
connection attempt. Connection is thus linked to a session directly on
its instantiation contrary to previously where no session existed until
listener_accept().

This patch will allow to extend rhttp usage. Most notably, it will be
useful to use various sample fetches on the server line and extend
logging capabilities.

Changes are minimal, yet consequences are considered not trivial as for
the first time a FE connection session is instantiated before
listener_accept(). This requires an extra explicit check in
session_accept_fd() to not overwrite an existing session. Also, flag
SESS_FL_RELEASE_LI is not set immediately as listener counters must note
be decremented if connection and its session are freed before reversal
is completed, or else listener counters will be invalid.

conn_session_free() is used as connection destroy callback to ensure the
session will be freed automatically on connection release.
2024-05-22 10:01:57 +02:00
Amaury Denoyelle
45b80aed70 MINOR: session: define flag to explicitely release listener on free
When a session is allocated for a FE connection, session_free() is
responsible to call listener_release() to decrement listener connection
counters and resume listening.

Until now, <listener> member of session was tested inside session_free()
before invocating listener_release(). To highlight more explicitely the
relation between sessions and listeners, introduce a new flag
SESS_FL_RELEASE_LI. Only session with such flag set will invoke
listener_release() on their cleanup. Flag is set inside
session_accept_fd() on success.

This patch has no functional change. However, it will be useful to
implement session creation for rHTTP preconnect.
2024-05-22 10:01:57 +02:00
Amaury Denoyelle
808daa7cfb BUG/MINOR: rhttp: fix task_wakeup state
TASK_WOKEN_ANY was incorrectly used as argument to task_wakeup() for
rhttp preconnect task. This value is used as a flag. Replace it by
proper individual values. This is labelled as a bug but it has no known
impact.

This should be backported up to 2.9.
2024-05-22 10:01:57 +02:00
Amaury Denoyelle
2770ef352e BUG/MINOR: rhttp: prevent listener suspend
Ensure "disable frontend" on a reverse HTTP listener is forbidden by
returing -1 on suspend callback. Suspending such a listener has unknown
effect and so is not properly implemented for now.

This should be backported up to 2.9.
2024-05-22 10:01:57 +02:00
Amaury Denoyelle
ceebb09744 BUG/MEDIUM: rhttp: fix preconnect on single-thread
On initialization of a rhttp bind, the first thread available on the
listener is selected to execute the first occurence of the preconnect
task.

This thread selection was incorrect as it used my_ffsl() which returns
value indexed from 1, contrary to tid which are indexed from 0. This
cause the first listener thread to be skipped in favor of the second
one. Worst, if haproxy runs in single-thread mode, calculated thread ID
will be invalid and the task will never run, which prevent any
preconnect execution.

Fix this by substracting the result of my_ffsl() by 1 to have a value
indexed from 0.

This must be backported up to 2.9.
2024-05-22 10:01:57 +02:00
Amaury Denoyelle
4f80543220 MINOR: rhttp: add log on connection allocation failure
Add an error log when new_reverse_conn() fails. This may help to
diagnose future issues on reverse HTTP.
2024-05-22 10:01:57 +02:00
Amaury Denoyelle
3efd9f3925 BUG/MINOR: server: free PROXY v2 TLVs on srv drop
Dynamically allocated servers PROXY TLVs were not freed on server
release. This patch fixes this leak by extending srv_free_params().
Every server line with set-proxy-v2-tlv-fmt keyword is impacted.

For static servers, issue is minimal as it will only cause leak on
deinit(). However, this could be aggravated when performing multiple
removal of dynamic servers.

This should be backported up to 2.9.
2024-05-22 10:01:57 +02:00
Amaury Denoyelle
8b72270e95 BUG/MINOR: connection: parse PROXY TLV for LOCAL mode
conn_recv_proxy() is responsible to parse PROXY protocol header. For v2
of the protocol, TLVs parsing is implemented. However, this step was
only done inside 'PROXY' command label. TLVs were never extracted for
'LOCAL' command mode.

Fix this by extracting TLV parsing loop outside of the switch case. Of
notable importance, tlv_offset is updated on LOCAL label to point to
first TLV location.

This bug should be backported up to 2.9 at least. It should even
probably be backported to every stable versions. Note however that this
code has changed much over time. It may be useful to use option
'--ignore-all-space' to have a clearer overview of the git diff.
2024-05-22 10:01:57 +02:00
Christopher Faulet
eb89a7da33 MAJOR: spoe: Let the SPOE back into the game
This reverts commits 885e40494c and
dff9807188.

We decided to spend some time to refactor and rationnalize the SPOE for the
3.1. Thus there is no reason to still consider it as deprecated for the
3.0. Compatibility between the both versions will be maintained.

See #2502 for more info.
2024-05-22 09:04:38 +02:00
Christopher Faulet
746e6f8597 BUG/MINOR: http-ana: Don't crush stream termination condition on internal error
When internal error is reported from an HTTP analyzer, we must take care to
not set the stream termination condition if it was already set. For
instance, it happens when a message rewrite fails. In this case
SF_ERR_PXCOND is set by the rule. The HTTP analyzer must not crush it with
SF_ERR_INTERNAL.

The regression was introduced with the commit 0fd25514d6 ("MEDIUM: http-ana:
Set termination state before returning haproxy response").

The bug was discovered working in the issue #2568. It must be backported to
2.9.
2024-05-22 09:04:38 +02:00
Valentine Krasnobaeva
39caa20b3c MINOR: sock: set conn->err_code in case of EPERM
To improve the readability of sock_handle_system_err(), let's
set explicitly conn->err_code as CO_ER_SOCK_ERR in case of EPERM
(could be returned by setns syscall).
2024-05-21 20:14:31 +02:00
Valentine Krasnobaeva
5f713c03be BUG/MEDIUM: proto: fix fd leak in <proto>_connect_server
This fixes the fd leak, introduced in the commit d3fc982cd7
("MEDIUM: proto: make common fd checks in sock_create_server_socket").

Initially sock_create_server_socket() was designed to return only created
socket FD or -1. Its callers from upper protocol layers were required to test
the returned errno and were required then to apply different configuration
related checks to obtained positive sock_fd. A lot of this code was duplicated
among protocols implementations.

The new refactored version of sock_create_server_socket() gathers in one place
all duplicated checks, but in order to be complient with upper protocol
layers, it needs the 3rd parameter: 'stream_err', in which it sets the
Stream Error Flag for upper levels, if the obtained sock_fd has passed all
additional checks.

No backport needed since this was introduced in 3.0-dev10.
2024-05-21 20:14:05 +02:00
William Lallemand
e732de7db2 DOC: configuration: update the crt-list documentation
Update the crt-list documentation with the supported keywords.

Also format it in a more clear way.

Must be backported to 2.8.
2024-05-21 18:30:45 +02:00
William Lallemand
e6657fd108 MEDIUM: ssl: don't load file by discovering them in crt-store
In commit 55e9e9591 ("MEDIUM: ssl: temporarily load files by detecting
their presence in crt-store"), ssl_sock_load_pem_into_ckch() was
replaced by ssl_sock_load_files_into_ckch() in the crt-store loading.

But the side effect was that we always try to autodetect, and this is
not what we want. This patch reverse this, and add specific code in the
crt-list loading, so we could autodetect in crt-list like it was done
before, but still try to load files when a crt-store filename keyword is
specified.

Example:

These crt-list lines won't autodetect files:

    foobar.crt [key foobar.key issuer foobar.issuer ocsp-update on] *.foo.bar
    foobar.crt [key foobar.key] *.foo.bar

These crt-list lines will autodect files:

    foobar.pem [ocsp-update on] *.foo.bar
    foobar.pem
2024-05-21 18:30:45 +02:00
Aurelien DARRAGON
22ec2ad8b0 DEBUG: fd: add name hint for large memory areas
Thanks to ("MINOR: tools: add vma_set_name() helper"), set a name hint
for large arrays created by fd api (fdtab arrays and so on) so that
that they can be easily identified in /proc/<pid>/maps.

Depending on malloc() implementation, such memory areas will normally be
merged on the heap under MMAP_THRESHOLD (128 kB by default) and will
have a dedicated memory area once the threshold is exceeded. As such, when
large enough, they will appear like this in /proc/<pid>/maps:

7b8e83200000-7b8e84201000 rw-p 00000000 00:00 0                          [anon:fd:fdinfo]
7b8e84400000-7b8e85401000 rw-p 00000000 00:00 0                          [anon:fd:polled_mask]
7b8e85600000-7b8e89601000 rw-p 00000000 00:00 0                          [anon:fd:fdtab_addr]
7b8e90a00000-7b8e90e01000 rw-p 00000000 00:00 0                          [anon:fd:fd_updt]
2024-05-21 17:55:29 +02:00
Aurelien DARRAGON
9424e5a06f DEBUG: errors: add name hint for startup-logs memory area
Thanks to ("MINOR: tools: add vma_set_name() helper"), set a name hint
for startup-logs ring's memory area created using mmap() so it can be
easily indentified in /proc/<pid>/maps.

7b8e91cce000-7b8e91cde000 rw-s 00000000 00:19 46                         [anon_shmem:errors:startup_logs]
2024-05-21 17:55:20 +02:00
Aurelien DARRAGON
abb8412d20 DEBUG: pollers: add name hint for large memory areas used by pollers
Thanks to ("MINOR: tools: add vma_set_name() helper"), set a name hint
for large memory areas allocated by pollers upon init so that they can
be easily indentified in /proc/<pid>/maps.

For now, only linux-compatible pollers are considered since vma_set_name()
requires a recent linux kernel (>= 5.17).

Depending on malloc() implementation, such memory areas will normally be
merged on the heap under MMAP_THRESHOLD (128 kB by default) and will
have a dedicated memory area once the threshold is exceeded. As such, when
large enough, they will appear like this in /proc/<pid>/maps:

7ec6b2d40000-7ec6b2d61000 rw-p 00000000 00:00 0                          [anon:ev_poll:fd_evts_wr]
7ec6b2d61000-7ec6b2d82000 rw-p 00000000 00:00 0                          [anon:ev_poll:fd_evts_rd]
2024-05-21 17:55:14 +02:00
Aurelien DARRAGON
6c5869f846 DEBUG: sink: add name hint for memory area used by memory-backed sinks
Thanks to ("MINOR: tools: add vma_set_name() helper"), set a name hint
for user created memory-backed sinks (ring sections without backing-file)
so that they can be easily indentified in /proc/<pid>/maps.

Depending on malloc() implementation, such memory areas will normally be
merged on the heap under MMAP_THRESHOLD (128 kB by default) and will
have a dedicated memory area once the threshold is exceeded. As such, when
large enough, they will appear like this in /proc/<pid>/maps:

7b8e8ac00000-7b8e8bf13000 rw-p 00000000 00:00 0                          [anon💍myring]
2024-05-21 17:55:09 +02:00
Aurelien DARRAGON
6de0da1b54 DEBUG: shctx: name shared memory using vma_set_name()
In 98d22f212 ("MEDIUM: shctx: Naming shared memory context"), David
implemented prctl/PR_SET_VMA support to give a name to shctx maps when
supported. Maps were named after "HAProxy $name". It turns out that it
is not relevant to include "HAProxy" in the map name, given that we're
already looking at maps for a given PID (and here it's HAProxy's pid).

Instead, let's name shctx maps by making use of the new vma_set_name()
helper introduced by previous commit. Resulting maps will be named
"shctx:$name", e.g.: "shctx:globalCache", they will appear like this in
/proc/<pid>/maps:

7ec6aab0f000-7ec6ac000000 rw-s 00000000 00:01 405                        [anon_shmem:shctx:custom_name]
2024-05-21 17:55:03 +02:00
Aurelien DARRAGON
51a8f134ef DEBUG: tools: add vma_set_name() helper
Following David Carlier's work in 98d22f21 ("MEDIUM: shctx: Naming shared
memory context"), let's provide an helper function to set a name hint on
a virtual memory area (ie: anonymous map created using mmap(), or memory
area returned by malloc()).

Naming will only occur if available, and naming errors will be ignored.
The function takes mandatory <type> and <name> parameterss to build the
map name as follow: "type:name". When looking at /proc/<pid>/maps, vma
named using this helper function will show up this way (provided that
the kernel has prtcl support for PR_SET_VMA_ANON_NAME):

example, using mmap + MAP_SHARED|MAP_ANONYMOUS:
  7364c4fff000-736508000000 rw-s 00000000 00:01 3540  [anon_shmem:type:name]
Another example, using mmap + MAP_PRIVATE|MAP_ANONYMOUS or using
glibc/malloc() above MMAP_THRESHOLD:
  7364c4fff000-736508000000 rw-s 00000000 00:01 3540  [anon:type:name]
2024-05-21 17:54:58 +02:00
Aurelien DARRAGON
0cfbeb1ae8 BUG/MINOR: ring: free ring's allocated area not ring's usable area when using maps
Since 40d1c84bf0 ("BUG/MAJOR: ring: free the ring storage not the ring
itself when using maps"), munmap() call for startup_logs's ring and
file-backed rings fails to work (EINVAL) and causes memory leaks during
process cleanup.

munmap() fails because it is called with the ring's usable area pointer
which is an offset from the underlying original memory block allocated
using mmap(). Indeed, ring_area() helper function was misused because
it didn't explicitly mention that the returned address corresponds to
the usable storage's area, not the allocated one.

To fix the issue, we add an explicit ring_allocated_area() helper to
return the allocated area for the ring, just like we already have
ring_allocated_size() for the allocated size, and we properly use both
the allocated size and allocated area to manipulate them using munmap()
and msync().

No backport needed.
2024-05-21 11:42:35 +02:00
William Lallemand
d74ba7cc24 MINOR: ssl: check parameter in ckch_conf_cmp()
Check prev and new parameters in ckch_conf_cmp() so we don't dereference
a NULL ptr. There is no risk since it's not used with a NULL ptr yet.

Also remove the check that are done later, and do it at the beginning of
the function.

Should fix issue #2572.
2024-05-21 11:09:59 +02:00
William Lallemand
140078c19d CLEANUP: ssl/cli: remove unused code in dump_crtlist_conf
This code was never used because space is never define before:

    if (space) chunk_appendf(buf, " ");

Should fix issue #2571.
2024-05-21 10:58:09 +02:00
William Lallemand
58103bc8e6 MINOR: ssl: ckch_conf_cmp() compare multiple ckch_conf structures
The ckch_conf_cmp() function allow to compare multiple ckch_conf
structures in order to check that multiple usage of the same crt in the
configuration uses the same ckch_conf definition.

A crt-list allows to use "crt-store" keywords that defines a ckch_store,
that can lead to inconsistencies when a crt is called multiple time with
different parameters.

This function compare and dump a list of differences in the err variable
to be output as error.

The variant ckch_conf_cmp_empty() compares the ckch_conf structure to an
empty one, which is useful for bind lines, that are not able to have
crt-store keywords.

These functions are used when a crt-store is already inialized and we
need to verify if the parameters are compatible.

ckch_conf_cmp() handles multiple cases:

- When the previous ckch_conf was declared with CKCH_CONF_SET_EMPTY, we
  can't define any new keyword in the next initialisation
- When the previous ckch_conf was declared with keywords in a crtlist
  (CKCH_CONF_SET_CRTLIST), the next initialisation must have the exact
  same keywords.
- When the previous ckch_conf was declared in a "crt-store"
  (CKCH_CONF_SET_CRTSTORE), the next initialisaton could use no keyword
  at all or the exact same keywords.
2024-05-17 17:35:51 +02:00
William Lallemand
1bc6e990f2 MEDIUM: ssl/cli: handle crt-store keywords in crt-list over the CLI
This patch adds crt-store keywords from the crt-list on the CLI.

- keywords from crt-store can be used over the CLI when inserting
  certificate in a crt-list
- keywords from crt-store are dumped when showing a crt-list content
  over the CLI

The ckch_conf_kws.func function pointer needed a new "cli" parameter, in
order to differenciate loading that come from the CLI or from the
startup, as they don't behave the same. For example it must not try to
load a file on the filesystem when loading a crt-list line from the CLI.

dump_crtlist_sslconf() was renamed in dump_crtlist_conf() and takes a
new ckch_conf parameter in order to dump relevant crt-store keywords.
2024-05-17 17:35:51 +02:00
William Lallemand
2bcf38c7c8 MEDIUM: ssl: add ocsp-update.disable global option
This option allow to disable completely the ocsp-update.

To achieve this, the ocsp-update.mode global keyword don't rely anymore
on SSL_SOCK_OCSP_UPDATE_OFF during parsing to call
ssl_create_ocsp_update_task().

Instead, we will inherit the SSL_SOCK_OCSP_UPDATE_* value from
ocsp-update.mode for each certificate which does not specify its own
mode.

To disable completely the ocsp without editing all crt entries,
ocsp-update.disable is used instead of "ocsp-update.mode" which is now
only used as the default value for crt.
2024-05-17 17:35:51 +02:00
William Lallemand
2e6615b282 MINOR: ssl: ckch_conf_clean() utility function for ckch_conf
- ckch_conf_clean() to free() the content of a ckch_conf structure,
  mostly the string that were strdup()
2024-05-17 17:35:51 +02:00
William Lallemand
2b6b7fea58 MINOR: ssl/ocsp: use 'ocsp-update' in crt-store
Use the ocsp-update keyword in the crt-store section. This is not used
as an exception in the crtlist code anymore.

This patch introduces the "ocsp_update_mode" variable in the ckch_conf
structure.

The SSL_SOCK_OCSP_UPDATE_* enum was changed to a define to match the
ckch_conf on/off parser so we can have off to -1.
2024-05-17 17:35:51 +02:00
William Lallemand
462e5b0098 MINOR: ssl: handle PARSE_TYPE_INT and PARSE_TYPE_ONOFF in ckch_store_load_files()
The callback used by ckch_store_load_files() only works with
PARSE_TYPE_STR.

This allows to use a callback which will use a integer type for PARSE_TYPE_INT
and PARSE_TYPE_ONOFF.

This require to change the type of the callback to void * to pass either
a char * or a int depending of the parsing type.

The ssl_sock_load_* functions were encapsuled in ckch_conf_load_*
function just to match the type.

This will allow to handle crt-store keywords that are ONOFF or INT
types.
2024-05-17 17:35:51 +02:00
William Lallemand
c5a665f5d8 MEDIUM: ssl: ckch_conf_parse() uses -1/0/1 for off/default/on
ckch_conf_parse() now set -1 for a off value and 1 for a on value.
This allow to detect when a value is the default since the struct are memset
to 0.
2024-05-17 17:35:51 +02:00
William Lallemand
2b8880e395 MINOR: ssl: pass ckch_store instead of ckch_data to ssl_sock_load_ocsp()
ssl_sock_put_ckch_into_ctx() and ssl_sock_load_ocsp() need to take a
ckch_store in argument. Indeed the ocsp_update_mode is not stored
anymore in ckch_data, but in ckch_conf which is part of the ckch_store.

This is a minor change, but the function definition had to change.
2024-05-17 17:35:51 +02:00
William Lallemand
db09c2168f CLEANUP: ssl/ocsp: remove the deprecated parsing code for "ocsp-update"
Remove the "ocsp-update" keyword handling from the crt-list.

The code was made as an exception everywhere so we could activate the
ocsp-update for an individual certificate.

The feature will still exists but will be parsed as a "crt-store"
keyword which will still be usable in a "crt-list". This will appear in
future commits.

This commit also disable the reg-tests for now.
2024-05-17 17:35:51 +02:00
William Lallemand
d616932076 MEDIUM: ssl/crtlist: loading crt-store keywords from a crt-list
This patch allows the usage of "crt-store" keywords from a "crt-list".

The crtstore_parse_load() function was splitted into 2 functions, so the
keywords parsing is done in ckch_conf_parse().

With this patch, crt are loaded with ckch_store_new_load_files_conf() or
ckch_store_new_load_files_path() depending on weither or not there is a
"crt-store" keyword.

More checks need to be done on "crt" bind keywords to ensure that
keywords are compatible.

This patch does not introduce the feature on the CLI.
2024-05-17 17:35:51 +02:00
William Lallemand
8526d666d2 MINOR: ssl: ckch_store_new_load_files_conf() loads filenames from ckch_conf
ckch_store_new_load_files_conf() is the equivalent of
new_ckch_store_load_files_path() but instead of trying to find the files
using a base filename, it will load them from a list of files.
2024-05-17 17:35:51 +02:00
Christopher Faulet
2fc9e6fa39 MEDIUM: mux-h1: Support C-L/T-E header suppressions when sending messages
During the 2.9 dev cycle, to be able to support zero-copy data forwarding, a
change on the H1 mux was performed to ignore the headers modifications about
payload representation (Content-Length and Transfer-Encoding headers).

It appears there are some use-cases where it could be handy to change values
of these headers or just remove them. For instance, we can imagine to remove
these headers on a server response to force the old HTTP/1.0 close mode
behavior. So thaks to this patch, the rules are relaxed. It is now possible
to remove these headers. When this happens, the following rules are applied:

 * If "Content-Length" header is removed but a "Transfer-Encoding: chunked"
   header is found, no special processing is performed. The message remains
   chunked. However the close mode is not forced.

 * If "Transfer-Encoding" header is removed but a "Content-Length" header is
   found, no special processing is performed. The payload length must comply
   to the specified content length.

 * If one of them is removed and the other one is not found, a response is
   switch the close mode and a "Content-Length: 0" header is forced on a
   request.

With these rules, we fit the best to the user expectations.

This patch depends on the following commit:

  * MINOR: mux-h1: Add a flag to ignore the request payload

This patch should fix the issue #2536. It should be backported it to 2.9
with the commit above.
2024-05-17 16:33:53 +02:00
Christopher Faulet
8e55d29109 MINOR: mux-h1: Add a flag to ignore the request payload
There was a flag to skip the response payload on output, if any, by stating
it is bodyless. It is used for responses to HEAD requests or for 204/304
responses. This allow rewrites during analysis. For instance a HEAD request
can be rewrite to a GET request for any reason (ie, a server not supporting
HEAD requests). In this case, the server will send a response with a
payload. On frontend side, the payload will be skipped and a valid response
(without payload) will be sent to the client.

With this patch we introduce the corresponding flag for the request. It will
be used to skip the request payload. In addition, when payload must be
skipped for a request or a response, The zero-copy data forwarding is now
disabled.
2024-05-17 16:33:53 +02:00
Christopher Faulet
45a45c917a BUG/MINOR: stats: Don't state the 303 redirect response is chunked
Start-line flags for 303-See-Other response returned by the stats applet are
not properly set. Indeed, the reponse has a "content-length" header but both
HTX_SL_F_CHNK and HTX_SL_F_CLEN flags are set. Because of this bug, the
reponse is considered as chunked. So, let's remove HTX_SL_F_CHNK flag.

And also add HTX_SL_F_BODYLESS flag because there is no payload
("content-length" header is always set to 0).

This patch must be backported to all stable versions. On the 2.8 and lower
versions, the commit d0b04920d1 ("BUG/MINOR: htpp-ana/stats: Specify that
HTX redirect messages have a C-L header") must be backported first.
2024-05-17 16:33:53 +02:00
Willy Tarreau
e362b076b1 Revert: MEDIUM: evports: permit to report multiple events at once"
Tests have shown that switching nevlist to global.tune.maxpollevents
is totally unreliable when using evports, and that events seem to be
missed. A good reproducer seems to be QUIC. There are not enough
users of Solaris to warrant spending more time trying to get down to
this, and even the few that remain are by definition not interested
in performance, so let's just revert the commit that tried to lift the
value: e6662bf706 ("MEDIUM: evports: permit to report multiple events
at once").

No backport is needed.
2024-05-17 15:57:18 +02:00
Aurelien DARRAGON
b9915a745e BUG/MEDIUM: fd: prevent memory waste in fdtab array
In 97ea9c49f1 ("BUG/MEDIUM: fd: always align fdtab[] to 64 bytes"), the
patch doesn't do what the message says. The intent was only to align the
base fdtab addr on 64 bytes so that all fdtab entries are aligned and thus
don't share the same cache line. For that, fdtab pointer is adjusted from
fdtab_addr (unaligned) address after it is allocated. Thus, all we need
is an extra 64 bytes in the fdtab_addr array for the aligment. Because
we use calloc() to perform the allocation, a dumb mistake was made: the
'+64' was added on <size> calloc argument, which means EACH fdtab entry
is allocated with 64 extra bytes.

Given that a single fdtab entry is 64 bytes, since 97ea9c49f1 each fdtab
entry now takes 128 bytes! We doubled fdtab memory consumption.

To give you an idea, on my laptop, when looking at memory consumption
using 'ps -p `pidof haproxy` -o size' right after starting haproxy
process with default settings (no maxsock enforced):

before 97ea9c49f1:
  -> 118440 (KB, ~= 118MB)

after 97ea9c49f1:
  -> 183976 (KB, ~= 184MB)

To fix this, use calloc with 1 <nmemb> and manually provide the size with
<size> as we would do if we used malloc(). With this patch, we're back to
pre-97ea9c49f1 for fdtab  memory consumption (with 64 extra bytes the
whole array, which is insignificant).

It should be backported to all stable versions.
2024-05-17 15:25:03 +02:00
Aurelien DARRAGON
e84c8dee1a BUILD: log: get rid of non-portable strnlen() func
In c614fd3b9 ("MINOR: log: add +cbor encoding option"), I wrongly used
strnlen() without noticing that the function is not portable (requires
_POSIX_C_SOURCE >= 2008) and that it was the first occurrence in the
entire project. In fact it is not a hard requirement since it's a pretty
simple function. Thus to restore build compatibility with minimal/older
build systems, let's actually get rid of it and use an equivalent portable
code where needed (we cannot simply rely on strlen() because the string
might not be NULL terminated, we must take upstream len into account).

No backport needed (unless c614fd3b9 gets backported)
2024-05-17 15:24:53 +02:00
William Lallemand
f18ed8d07e MEDIUM: ssl: add ocsp-update.mindelay and ocsp-update.maxdelay
This patch deprecates tune.ssl.ocsp-update.* in favor of
"ocsp-update.*".

Since the ocsp-update is not really a tunable of the SSL connections.
2024-05-17 15:00:11 +02:00
Amaury Denoyelle
fbc3d46b9f BUILD: stats: remove non portable getline() usage
getline() was used to read stats-file. However, this function is not
portable and may cause build issue on some systems. Replace it by
standard fgets().

No need to backport.
2024-05-17 14:53:19 +02:00
William Lallemand
ee58fac1b4 MINOR: ssl: rename tune.ssl.ocsp-update.mode in ocsp-update.mode
Since the ocsp-update is not strictly a tuning of the SSL stack, but a
feature of its own, lets rename the option.

The option was also missing from the index.
2024-05-17 14:50:00 +02:00
Amaury Denoyelle
0d35f8d918 MINOR: h3: report glitch on RFC violation
Increment glitch connection counter on every HTTP/3 or QPACK errors
which is a violation of the specification. This could be useful to get
rid early of bogus clients.
2024-05-16 10:58:54 +02:00
Amaury Denoyelle
216f70f989 MINOR: mux-quic: support glitches
Implement basic support for glitches on QUIC multiplexer. This is mostly
identical too glitches for HTTP/2.

A new configuration option named tune.quic.frontend.glitches-threshold
is defined to limit the number of glitches on a connection before
closing it.

Glitches counter is incremented via qcc_report_glitch(). A new
qcc_app_ops callback <report_susp> is defined. On threshold reaching, it
allows to set an application error code to close the connection. For
HTTP/3, value H3_EXCESSIVE_LOAD is returned. If not defined, default
code INTERNAL_ERROR is used.

For the moment, no glitch are reported for QUIC or HTTP/3 usage. This
will be added in future patches as needed.
2024-05-16 10:58:20 +02:00
Amaury Denoyelle
a6993a669b MINOR: h3: adjust error reporting on receive
This commit is the second step to simplify HTTP/3 error management. This
times it deals with receive side on h3_rcv_buf().

Various internal HTTP/3 to HTX conversion functions does not set
H3_INTERNAL_ERROR on h3c err anymore. Only standard error code are set.
For every errors, both internal and protocol ones, a negative value is
returned. This ensure that h3_rcv_buf() looping is interrupted. This
function will then set H3_INTERNAL_ERROR only if no standard error is
registered via h3c or h3s.

Along the previous commit, this should better reflect internal errors
from protocol ones caused by a faulty client.
2024-05-16 10:31:17 +02:00
Amaury Denoyelle
079d13f73f MINOR: h3: adjust error reporting on sending
It's currently difficult to differentiate HTTP/3 standard protocol
violation from internal issues which use solely H3_INTERNAL_ERROR code.
This patch aims is the first step to simplify this. The objective is to
reduce H3_INTERNAL_ERROR. <err> field of h3c should be reserved
exclusively to other values.

Simplify error management in sending via h3_snd_buf(). Sending side is
straightforward as only internal errors can be encountered. Do not
manually set h3c.err to H3_INTERNAL_ERROR in HTX to HTTP/3 various
conversion function. Instead, just return a negative value which is
enough to break h3_snd_buf() loop. H3_INTERNAL_ERROR is thus positionned
on a single location in this function for all sending operations.
2024-05-16 10:31:17 +02:00
Amaury Denoyelle
e094412337 MINOR: h3/qpack: adjust naming for errors
Rename enum values used for HTTP/3 and QPACK RFC defined codes. First
uses a prefix H3_ERR_* which serves as identifier between them. Also
separate QPACK values in a new dedicated enum qpack_err. This is deemed
cleaner.
2024-05-16 10:31:17 +02:00
Amaury Denoyelle
2dabcf30be MINOR: qpack: prepare error renaming
There is two distinct enums both related to QPACK error management. The
first one is dedicated to RFC defined code. The other one is a set of
internal values returned by qpack_decode_fs(). There has been issues
discovered recently due to the confusion between them.

Rename internal values with the prefix QPACK_RET_*. The older name
QPACK_ERR_* will be used in a future commit for the first enum.
2024-05-16 10:31:17 +02:00
Christopher Faulet
25bcdb1d95 BUG/MAJOR: h1: Be stricter on request target validation during message parsing
As stated in issue #2565, checks on the request target during H1 message
parsing are not good enough. Invalid paths, not starting by a slash are in
fact parsed as authorities. The same error is repeated at the sample fetch
level. This last point is annoying because routing rules may be fooled. It
is also an issue when the URI or the Host header are updated.

Because the error is repeated at different places, it must be fixed. We
cannot be lax by arguing it is the server's job to accept or reject invalid
request targets. With this patch, we strengthen the checks performed on the
request target during H1 parsing. Idea is to reject invalid requests at this
step to be sure it is safe to manipulate the path or the authority at other
places.

So now, the asterisk-form is only allowed for OPTIONS and OTHER methods.
This last point was added to not reject the H2 preface. In addition, we take
care to have only one asterisk and nothing more. For the CONNECT method, we
take care to have a valid authority-form. All other form are rejected. The
authority-form is now only supported for CONNECT method. No specific check
is performed on the origin-form (except for the CONNECT method). For the
absolute-form, we take care to have a scheme and a valid authority.

These checks are not perfect but should be good enough to properly identify
each part of the request target for a relative small cost. But, it is a
breaking change. Some requests are now be rejected while they was not on
older versions. However, nowadays, it is most probably not an issue.  If it
turns out it's really an issue for legitimate use-cases, an option would be
to supports these kinds of requests when the "accept-invalid-http-request"
option is set, with the consequence of seeing some sample fetches having an
unexpected behavior.

This patch should fix the issue #2665. It MUST NOT be backported. First
because it is a breaking change. And then because by avoiding backporting
it, it remains possible to relax the parsing with the
"accept-invalid-http-request" option.
2024-05-15 21:20:37 +02:00
Christopher Faulet
d3d9d83f03 BUG/MEDIUM: h1: Reject CONNECT request if the target has a scheme
The target of a CONNECT request must not have scheme. However, this was not
checked during the message parsing. It is now rejected.

This patch may be backported as far as 2.4.
2024-05-15 21:20:37 +02:00
Christopher Faulet
d724b0d147 BUG/MINOR: h1: Check authority for non-CONNECT methods only if a scheme is found
When a non-CONNECT H1 request is parsed, the authority is compared to the
host header value, to validate that they are the same. However there is an
issue here when a relative path is used (not begining with a '/'). In this
case, the path is considered as the authority and will be erroneously
compared to the host header value. It is observable with this kind of
request:

  GET admin HTTP/1.1
  Host: www.mysite.com

In this case "admin" is parsed as an authority while it is in fact a path.
At this step, it is not a big deal because it just happens on the very first
checks on the message during the parsing. However, the same happens when the
authority is updated. This will be fixed in another commit

Note this kind of request is invalid because the path does not start with a
'/'. But, till now, HAProxy does not reject it.

This patch is related to issue #2565. It must be backported as far as 2.4.
2024-05-15 21:20:37 +02:00
Willy Tarreau
821a04377d BUG/MEDIUM: muxes: enforce buf_wait check in takeover()
The ->takeover() is quite tricky. It didn't take care of the possibility
that the original thread's connection handler had been woken up to handle
an event (e.g. read0), failed to get a buffer, registered against its own
thread's buffer_wait queue and left the connection in an idle state.

A new thread could then come by, perform a takeover(), and when a buffer
was available, the new thread's tasklet would be woken up by the old one
via *_buf_available(), causing all sort of problems. These problems are
easy to reproduce, by running with shared backend connections and few
buffers (tune.buffers.limit=20, 8 threads, 500 connections, transfer
64kB objects and wait 2-5s for a crash to appear).

A first estimated solution consisted in removing the connection from the
idle list but it turns out that it would be worse for the delete stuff
(the connection no longer appearing as idle, making it impossible to find
it in order to close it). Also, idle counts wouldn't match anymore the
list's state, and the special case of private connections could be
difficult to handle as the connection could be forcefully re-added to the
idle list after allocation despite being private.

After multiple attempts to address the problem in various ways, it appears
that the only reliable solution for now (without starting to turn many
lists to mt_lists) is to have the takeover() function handle the buf_wait
detection or unregistration itself:

  - when doing a regular takeover aiming at finding an idle connection
    for a new request, connections that are blocked in a buffer_wait
    queue are quite rare and not interesting at all (since not immediately
    usable), so skipping them is sufficient. For this we detect that the
    desired connection belongs to a buffer_wait list by checking its
    buf_wait.list element. Note that this check is *not* thread-safe! The
    LIST_DEL_INIT() is performed by __offer_buffers() after the callback
    was called. But this is sufficient as it is now because the only way
    for the element to be seen as not in a list is after the element was
    last touched by __offer_buffers(), so the situation for this connection
    will not change in a different way later.

  - when doing a server delete, we're running under thread isolation.
    The connection might get taken over to be killed. The only trick is
    that private connections not belonging to any idle list may also
    experience this, and in this case even the idle_conns lock will not
    offer any protection against anything. But since we're run under
    thread isolation, we're certain not to compete with the other thread,
    so it's safe to directly unregister the connection from its owner
    thread. Normally this is already handled by conn_release() in
    cli_parse_delete_server(), which calls mux->destroy(), but this would
    actually update the current thread's queue instead of the origin
    thread's, thus we do need to perform an explicit dequeue before
    completing the takeover.

With this, the problem now looks solved for HTTP/1, HTTP/2 and FCGI,
though extensive tests were essentially run on HTTP/1 and HTTP/2.

While the problem has been there for a very long time, there should be
no reason to backport it since buffer_wait didn't practically work
before 3.0-dev and the process used to freeze hard very quickly before
we'd even have a chance to meet that race.
2024-05-15 19:37:12 +02:00
Willy Tarreau
edb99e296d BUG/MINOR: ssl_sock: fix xprt_set_used() to properly clear the TASK_F_USR1 bit
In 2.4-dev8 with commit 5c7086f6b0 ("MEDIUM: connection: protect idle
conn lists with locks"), the idle conns list started to be protected
using the lock for takeover, and the SSL layer used to always take
that lock. Later in 2.4-dev11, with commit 4149168255 ("MEDIUM: ssl:
implement xprt_set_used and xprt_set_idle to relax context checks"), we
decided to relax this lock using TASK_F_USR1 just as is done in muxes.

However the xprt_set_used() call, that's supposed to clear the flag,
visibly suffered from a copy-paste and kept the OR operation instead of
the AND, resulting in the flag never being released, so that SSL on the
backend continues to take the lock on each and every I/O access even when
the connection is not idle.

The effect is only a reduced performance. This could be backported, but
given the non-zero risk of triggering another bug somewhere, it would
be prudent to wait for this fix to be sufficiently tested in new
versions first.
2024-05-15 19:37:12 +02:00
Amaury Denoyelle
86aafd0236 BUG/MINOR: qpack: fix error code reported on QPACK decoding failure
qpack_decode_fs() is used to decode QPACK field section on HTTP/3
headers parsing. Its return value is incoherent as it returns either
QPACK_DECOMPRESSION_FAILED defined in RFC 9204 or any other internal
values defined in qpack-dec.h. On failure, such return code is reused by
HTTP/3 layer to be reported via a CONNECTION_CLOSE frame. This is
incorrect if an internal error values was reported as it is not defined
by any specification.

Fir return values of qpack_decode_fs() in two ways. Firstly, fix invalid
usages of QPACK_DECOMPRESSION_FAILED when decoded content is too large
for the correct internal error QPACK_ERR_TOO_LARGE.

Secondly, adjust qpack_decode_fs() API to only returns internal code
values. A new internal enum QPACK_ERR_DECOMP is defined to replace
QPACK_DECOMPRESSION_FAILED. Caller is responsible to convert it to a
suitable error value. For other internal values, H3_INTERNAL_ERROR is
used. This is done through a set of convert functions.

This should be backported up to 2.6. Note that trailers are not
supported in 2.6 so chunk related to h3_trailers_to_htx() can be safely
skipped.
2024-05-15 16:07:15 +02:00
Amaury Denoyelle
4295dd21bd BUG/MINOR: mux-quic: fix error code on shutdown for non HTTP/3
qcc_shutdown() is called whenever the connection must be closed. If
application protocol defined its owned shutdown callback, it is invoked
to use the correct error code. Else transport error code NO_ERROR is
used.

A bug occurs in the latter case as NO_ERROR is used with quic_err_app()
which is reserved for application errro codes. This will trigger the
emission of a CONNECTION_CLOSE of type 0x1d (Application) instead of
0x1c (Transport).

This bug is considered minor as it does not impact QUIC with HTTP/3. It
may only be visible when using experimental HTTP/0.9 protocol.

This should be backported up to 2.6. For 2.6, patch must be completed
rewritten due to code differences. Here is the change to apply :

  diff --git a/src/mux_quic.c b/src/mux_quic.c
  index 26fb70ddf..c48f82e27 100644
  --- a/src/mux_quic.c
  +++ b/src/mux_quic.c
  @@ -1918,7 +1918,9 @@ static void qc_release(struct qcc *qcc)
                          qc_send(qcc);
                  }
                  else {
  -                       qcc_emit_cc_app(qcc, QC_ERR_NO_ERROR, 0);
  +                       /* Duplicate from qcc_emit_cc_app() for Transport error code. */
  +                       if (!(qcc->conn->handle.qc->flags & QUIC_FL_CONN_IMMEDIATE_CLOSE))
  +                               qcc->conn->handle.qc->err = quic_err_transport(QC_ERR_NO_ERROR);
                  }
          }
2024-05-15 16:03:01 +02:00
Amaury Denoyelle
412f1eeb89 BUG/MEDIUM: server: clear purgeable conns before server deletion
Since the following commit, idle connections are cleared before a server
is deleted. This is better than blocking server deletion due to inactive
connections :

  6e0afb2e27
  MEDIUM: server: close idle conn on server deletion

A BUG_ON() has been added to ensure that server idle conn counter is nul
after these connections are removed. However, Willy managed to trigger
it easily by repeatedly and randomly delete servers accross a
single-thread haproxy using a server-template with 1000 instances. In
parallel, a h1load client is executed to generate traffic.

This BUG_ON() reflected that it some connections referencing the server
targetted for deletion remained, even though idle server list is empty.
In fact, this is caused by connections scheduled for purging. These
connections are moved from idle server list to a global toremove_list
while still being accounted by the server.

A first approach could be to decrement server idle counter while moving
connection to the purge list. However, this is functionnaly incorrect as
these purgeable connections still reference the server and it could
cause a crash if cleared after it.

The correct fix for this issue is simply to remove every purgeable
connections before a server is deleted. This is implemented by this
patch by extending cli_parse_delete_server(). It could be enough to only
remove connections targetted the deleted server, but as these
connections will be purged anyway it is justified to clear the whole
list.

This must not be backported, unless the above mentionned patch is.
2024-05-15 15:01:55 +02:00
Aurelien DARRAGON
231d3d32be MEDIUM: hlua: take nbthread into account in hlua_get_nb_instruction()
Based on Willy's idea (from 3.0-dev6 announcement message): in this patch
we try to reduce the max latency that can be caused by running lua scripts
with default settings.

Indeed, by default, hlua engine is allowed to process up to 10k
instructions per batch. While this value was found to be the optimal one
for a single thread, it turns out that keeping a thread busy for 10k lua
instructions could increase thread contention. This is especially true
when the script is loaded with 'lua-load', because in that case the
current thread owns the main lua lock and prevent other threads from
making any progress if they're also waiting on the main lock.

Thanks to Thierry Fournier's work, we know that performance-wise we can
reach optimal performance by sticking between 500 and 10k instructions
per batch. Given that, when the script is loaded using 'lua-load', if no
"tune.lua.forced-yield" was set by the user, we automatically divide the
default value (10K) by the number of threads haproxy can use to reduce
thread contention (given that all threads could compete for the main lua
lock), however we make sure not to return a value below 500, because
Thierry's work showed that this would come with a significant performance
loss.

The historical behavior may still be enforced by setting
"tune.lua.forced-yield" to 10000 in the global config section.
2024-05-15 11:59:44 +02:00
Aurelien DARRAGON
e60d9dddf8 MINOR: hlua: add hlua_nb_instruction getter
No functional behavior change, but this will ease the work of dynamically
computing hlua_nb_instruction value depending on various inputs.
2024-05-15 11:59:37 +02:00
Tim Duesterhus
6610f656ea DOC: Update UUID references to RFC 9562
When support for UUIDv7 was added in commit
aab6477b67
the specification still was a draft.

It has since been published as RFC 9562.

This patch updates all UUID references from the obsoleted RFC 4122 and the
draft for RFC 9562 to the published RFC 9562.
2024-05-15 11:40:08 +02:00
William Manley
366b722f7e MINOR: rhttp: Don't require SSL when attach-srv name parsing
An attach-srv config line usually looks like this:
    tcp-request session attach-srv be/srv name ssl_c_s_dn(CN)

while a rhttp server line usually looks like this:
    server srv rhttp@ sni req.hdr(host)

The server sni argument is used as a key for looking up connection in
the connection pool. The attach-srv name argument is used as a key for
inserting connections into the pool. For it to work correctly they must
match. There was a check that either both the attach-srv and server
provide that key or neither does.

It also checked that SSL and SNI was activated on the server. However,
thanks to current connect_server() implementation, it appears that SNI
is usable even without SSL to identify a connection in the pool. Thus,
it can be diverted from its original intent in reverse HTTP case to
serve even without SSL activated. For example, this could be useful to
use `fc_pp_unique_id` as a name expression (DISCLAIMER: note that for
now PROXY protocol is not compatible with rhttp).

Error is still reported if either SNI or name is used without the other.
This patch adjust the message to a more helpful one.

Arguably it would be easier to understand if instead of using `name` and
`sni` for `attach-srv` and `server` rules it used the same term in both
places - like "conn-pool-key" or something. That would make it clear
that the two must match.
2024-05-14 16:39:07 +02:00
Aurelien DARRAGON
32f0cd3242 BUG/MINOR: log: smp_rgs array issues with inherited global log directives
When a log directive is defined in the global section, each time we use
"log global" in a proxy section, the global log directives are duplicated
for the current proxy. This works by creating a new proxy logger struct
and duplicating every members for each global one.

However, smp_rgs logger member is a special pointer member that is
allocated when "range" is used on a log directive. Currently, we simply
copy the array pointer (from the global one), instead of creating our own
copy. Because of that, range log sampling may not work properly in some
situations prior to 3f1284560 ("MINOR: log: remove the unused curr_idx in
struct smp_log_range") when used in global log directives, for instance:

  global
    log 127.0.0.1:5114 format raw sample 1-2,3:4 local0 info # should receive 75% of all proxy logs
    log 127.0.0.1:5115 format raw sample 4:4 local0 info     # should receive 25% of all proxy logs

  listen proxy1
    log global

  listen proxy2
    log global

May not work as expected, because curr_idx was stored within smp_rgs array
member prior to 3f1284560, and due to this bug, it happens to be shared
between every log directive inherited from a "global" one. The result is
that curr_idx counter will not behave properly because the index will be
increased globally instead of per-log directive, and it could even suffer
from concurrent thread accesses under load since we don't own the global
log directive's lock when manipulating it.

Another issue that was revealed because of this bug is that the smp_rgs
array allocated during config parsing is never freed in free_logger(),
resulting in small memory leak during clean exit.

To fix these issues all at once, let's properly duplicate smp_rgs logger
struct member in dup_logger() like we already do for other special members
so that every log directive have its own sms_rgs copy, and then
systematically free it in free_logger().

While this bug affects all stable versions (including 2.4), it's probably
best to not backport this beyond 2.6 because of 211ea252d
("BUG/MINOR: logs: fix logsrv leaks on clean exit") prerequisite that
first appears in 2.6.

[ada: for versions prior to 2.9, 969e212
 ("MINOR: log: add dup_logsrv() helper function") and 76acde91
 ("BUG/MINOR: log: keep the ref in dup_logger()") must be backported
 first.
 Note: Some ctx adjustments should be performed because 'logger' struct
 used to be named 'logsrv' in the past and 2.9 introduced logger target
 struct member. Thus it's probably easier to manually apply 76acde91 and
 the current bugfix by hand directly on top of 969e212.
]
2024-05-14 12:00:23 +02:00
Aurelien DARRAGON
9d4a44e713 BUG/MINOR: log: fix leak in add_sample_to_logformat_list() error path
If add_sample_to_logformat_list() fails to allocate new logformat_node,
then we directly jump to error_free label to cleanup the node using
free_logformat_node() before returning an error.

However if the node failed to allocate, then the sample expression that
was allocated just before (not yet assigned) isn't released
(free_logformat_node() is a no-op when NULL is provided). Thus if expr
wasn't assigned to the node during early failure, then it must be manually
released.

This bug was introduced by 2462e5bcc ("BUG/MINOR: log: fix potential
lf->name memory leak") which wasn't marked for backports. It only
affects 3.0.
2024-05-13 16:44:27 +02:00
Willy Tarreau
0ce51dc93b MEDIUM: dynbuf: implement emergency buffers
The buffer reserve set by tune.buffers.reserve has long been unused, and
in order to deal gracefully with failed memory allocations we'll need to
resort to a few emergency buffers that are pre-allocated per thread.

These buffers are only for emergency use, so every time their count is
below the configured number a b_free() will refill them. For this reason
their count can remain pretty low. We changed the default number from 2
to 4 per thread, and the minimum value is now zero (e.g. for low-memory
systems). The tune.buffers.limit setting has always been a problem when
trying to deal with the reserve but now we could simplify it by simply
pushing the limit (if set) to match the reserve. That was already done in
the past with a static value, but now with threads it was a bit trickier,
which is why the per-thread allocators increment the limit on the fly
before allocating their own buffers. This also means that the configured
limit is saner and now corresponds to the regular buffers that can be
allocated on top of emergency buffers.

At the moment these emergency buffers are not used upon allocation
failure. The only reason is to ease bisecting later if needed, since
this commit only has to deal with resource management.
2024-05-10 17:18:13 +02:00
Willy Tarreau
47665be083 MEDIUM: mux-h1: allocate without queuing when retrying
Now when trying to allocate a buffer, we can check if we've been notified
of availability via the callback, in which case we should not consult the
queue, or if we're doing a first allocation and check the queue. At this
point it still doesn't change much since the stream still doesn't make use
of it but some progress is expected.
2024-05-10 17:18:13 +02:00
Willy Tarreau
b5714b45e8 MEDIUM: stream: allocate without queuing when retrying
Now when trying to allocate the work buffer, we can check if we've been
notified of availability via the buf_wait callback, in which case we
should not consult the queue, or if we're doing a first allocation and
check the queue.
2024-05-10 17:18:13 +02:00
Willy Tarreau
f552f79ba5 MINOR: mux-h1: report that a buffer allocation succeeded
When the buffer allocation callback is notified of a buffer availability,
it will now set a MAYALLOC flag in addition to clearing the ALLOC one, for
each of the 3 levels where we may fail an allocation. The flag will be
cleared upon a successful allocation. This will soon be used to decide to
re-allocate without waiting again in the queue. For now it has no effect.

There's just a trick, we need to clear the various *_ALLOC flags before
testing h1_recv_allowed() otherwise it will return false!
2024-05-10 17:18:13 +02:00
Willy Tarreau
cb2d758043 MINOR: applet: report about buffer allocation success
When appctx_buf_available() is called, it now sets APPCTX_FL_IN_MAYALLOC
or APPCTX_FL_OUT_MAYALLOC depending on the reportedly permitted buffer
allocation, and these flags are cleared when the said buffers are
allocated. For now they're not used for anything else.
2024-05-10 17:18:13 +02:00
Willy Tarreau
17d8916bb1 MINOR: stream: report that a buffer allocation succeeded
When the buffer allocation callback is notified of a buffer availability,
it will now set a MAYALLOC flag on the stream so that the stream knows it
is allowed to bypass the queue checks. For now this is not used.
2024-05-10 17:18:13 +02:00
Willy Tarreau
a160b3c50c MEDIUM: dynbuf/mux-h1: do not allocate the buffers in the callback
One of the problematic designs with the buffer_wait mechanism is that
the callbacks pre-allocate the buffers and stay in the run queue for
a while, resulting in all of the few buffers being assigned to waiting
tasks instead of being all available to one task that needs them all at
once.

Here we simply stop doing this, the callback clears the waiting flags
and wakes the task up so that it has a chance of still finding some
buffers.
2024-05-10 17:18:13 +02:00
Willy Tarreau
c510e81a3f MINOR: dynbuf/mux-h1: use different criticalities for buffer allocations
While it could certainly still be improved, this first approach consists
in assigning buffers like this in the H1 mux:
  - h1c->obuf : DB_MUX_TX
  - h1c->ibuf : DB_MUX_RX
  - h1s->rxbuf: DB_SE_RX

That's done via 3 distinct functions for better code clarity, and it
also allowed to move the missing buffer flags assignment there.

Among possible improvements would be to take into consideration the
state of the parser (i.e. no data yet vs data, or headers vs payload)
so that even server beginning of response or pure payload can be lowered
in priority.
2024-05-10 17:18:13 +02:00
Willy Tarreau
4ffb3b5ebe MINOR: applet: set the blocking flag in the buffer allocation function
Instead of having each caller of appctx_get_buf() think about setting
the blocking flag, better have the function do it, since it's already
handling the queue anyway. This way we're sure that both are consistent.
2024-05-10 17:18:13 +02:00
Willy Tarreau
ee0d56ac85 MEDIUM: applet: make appctx_buf_available() only wake the applet up, not allocate
Now we don't want bufwait handlers to preallocate the resources they
were expecting since it contributes to the shortage. Let's just wake
the applet up and that's all.
2024-05-10 17:18:13 +02:00
Willy Tarreau
9a27d7aa6f MEDIUM: dynbuf/stream: do not allocate the buffers in the callback
One of the problematic designs with the buffer_wait mechanism is that
the callbacks pre-allocate the buffers and stay in the run queue for
a while, resulting in all of the few buffers being assigned to waiting
tasks instead of being all available to one task that needs them all at
once.

Here we simply stop doing this, the callback clears the waiting flags
and wakes the task up so that it has a chance of still finding some
buffers.
2024-05-10 17:18:13 +02:00
Willy Tarreau
db21062881 MEDIUM: dynbuf/stream: re-enable queueing upon failed buffer allocation
The errors were not working fine anyway since we know that upon low memory
condition everything freezes. However we have a chance to do better now,
so let's start by re-enabling queueing when allocations fail.
2024-05-10 17:18:13 +02:00
Willy Tarreau
f5566afec6 MEDIUM: dynbuf: generalize the use of b_dequeue() to detach buffer_wait
Now thanks to this the bufq_map field is expected to remain accurate.
2024-05-10 17:18:13 +02:00
Willy Tarreau
a5d6a79986 MEDIUM: dynbuf: make the buffer_wq an array of list heads
Let's turn the buffer_wq into an array of 4 list heads. These are chosen
by criticality. The DB_CRIT_TO_QUEUE() macro maps each criticality level
into one of these 4 queues. The goal here clearly is to make it possible
to wake up the most critical queues in priority in order to let some tasks
finish their job and release buffers that others can use.

In order to avoid having to look up all queues, a bit map indicates which
queues are in use, which also allows to avoid looping in the most common
case where queues are empty..
2024-05-10 17:18:13 +02:00
Willy Tarreau
a214197ce7 MINOR: dynbuf: use the b_queue()/b_requeue() functions everywhere
The code places that were used to manipulate the buffer_wq manually
now just call b_queue() or b_requeue(). This will simplify the multiple
list management later.
2024-05-10 17:18:13 +02:00
Willy Tarreau
72d0dcda8e MINOR: dynbuf: pass a criticality argument to b_alloc()
The goal is to indicate how critical the allocation is, between the
least one (growing an existing buffer ring) and the topmost one (boot
time allocation for the life of the process).

The 3 tcp-based muxes (h1, h2, fcgi) use a common allocation function
to try to allocate otherwise subscribe. There's currently no distinction
of direction nor part that tries to allocate, and this should be revisited
to improve this situation, particularly when we consider that mux-h2 can
reduce its Tx allocations if needed.

For now, 4 main levels are planned, to translate how the data travels
inside haproxy from a producer to a consumer:
  - MUX_RX:   buffer used to receive data from the OS
  - SE_RX:    buffer used to place a transformation of the RX data for
              a mux, or to produce a response for an applet
  - CHANNEL:  the channel buffer for sync recv
  - MUX_TX:   buffer used to transfer data from the channel to the outside,
              generally a mux but there can be a few specificities (e.g.
              http client's response buffer passed to the application,
              which also gets a transformation of the channel data).

The other levels are a bit different in that they don't strictly need to
allocate for the first two ones, or they're permanent for the last one
(used by compression).
2024-05-10 17:18:13 +02:00
Amaury Denoyelle
cc9827bb09 BUG/MEDIUM: mux-quic: fix crash on STOP_SENDING received without SD
Abort reason code received on STOP_SENDING is notified to upper layer
since the following commit :
  367ce1ebf3
  MINOR: mux-quic: Set tha SE abort reason when a STOP_SENDING frame is received

However, this causes a crash when a STOP_SENDING is received on a QCS
instance without any stream instantiated. Fix this by checking first if
qcs->sd is not NULL before setting abort code.

This bug can easily be reproduced by emitting a STOP_SENDING as first
frame of a stream.

This should fix github issue #2563.

This does not need to be backported.
2024-05-10 11:01:05 +02:00
Aurelien DARRAGON
fbbc2925d4 BUG/MEDIUM: log/ring: broken syslog octet counting
As reported by Tristan in GH #2561, syslog messages sent over rings are
malformed since commit 01aa0a05 ("MEDIUM: ring: change the ring reader
to use the new vector-based API now").

Indeed, take a look at the following log message produced prior to
01aa0a05:

  181 <134>1 2024-05-07T09:45:21.543263+02:00 - haproxy 113700 - - 127.0.0.1:56136 [07/May/2024:09:45:21.491] front front/s1 0/0/21/30/51 404 369 - - ---- 1/1/0/0/0 0/0   "GET / HTTP/1.1"

Starting with 01aa0a05, here's the equivalent log message:

  <134>1 2024-05-07T09:45:21.543263+02:00 - haproxy 112729 - - 127.0.0.1:56136 [07/May/2024:09:45:21.491] front front/s1 0/0/66/39/105 404 369 - - ---- 1/1/0/0/0 0/0   "GET / HTTP/1.1"-fwr

-> Message is missing octet counting header, and garbage bytes are found
at the end of the payload.

This bug is caused by a small mistake in syslog_applet_append_event():
when the function was refactored to use vector API instead of buffer
API, we used 'trash.area' as starting pointer to write the event instead
of 'trash.area + trash.data', causing existing octet counting prefix
(already written in trash) to be overwritten and trash.data to be
wrongly incremented.

No backport needed (01aa0a05 was introduced during 3.0 development)
2024-05-07 19:23:01 +02:00
Christopher Faulet
bd47e344b8 MINOR: connection: Add samples to retrieve info on streams for a connection
Thanks to the previous fix, it is now possible to get the number of opened
streams for a connection and the negociated limit. Here, corresponding
sample feches are added, in fc_ and bc_ scopes.

On frontend side, the limit of streams is imposed by HAProxy. But on the
backend side, the limit is defined by the server. it may be useful for
debugging purpose because it may explain slow-downs on some processing.
2024-05-06 22:00:01 +02:00
Christopher Faulet
eca9831ec8 MINOR: muxes: Add ctl commands to get info on streams for a connection
There are 2 new ctl commands that may be used to retrieve the current number
of streams openned for a connection and its limit (the maximum number of
streams a mux connection supports).

For the PT and H1 muxes, the limit is always 1 and the current number of
streams is 0 for idle connections, otherwise 1 is returned.

For the H2 and the FCGI muxes, info are already available in the mux
connection.

For the QUIC mux, the limit is also directly available. It is the maximum
initial sub-ID of bidirectional stream allowed for the connection. For the
current number of streams, it is the number of SC attached on the connection
and the number of not already attached streams present in the "opening_list"
list.
2024-05-06 22:00:00 +02:00
Christopher Faulet
12fb6d73cd MINOR: mux-quic: Add .ctl callback function to get info about a mux connection
Other muxes implement this callback function. It was not implemented for the
QUIC mux because it was useless. It will be used to retrieve the current/max
number of stream for a quic connection. So let's added it, adding the
default support for MUX_CTL_EXIT_STATUS command.
2024-05-06 22:00:00 +02:00
Christopher Faulet
068ce2d5d2 MINOR: stconn: Add samples to retrieve about stream aborts
It is now possible to retrieve some info about the abort received for a
server or a client stream, if any.

  * fs.aborted and bs.aborted can be used to know if an abort was received
    on frontend or backend side. A boolean is returned.

  * fs.rst_code and bs.rst_code return the code of the received RESET_STREAM
    frame for a H2 stream or the code of the received STOP_SENDING frame for
    a QUIC stream. In both cases, the error code attached to the frame is
    returned. The sample fetch fails if no such frame was received or if the
    stream is not an H2/QUIC stream.
2024-05-06 22:00:00 +02:00
Christopher Faulet
367ce1ebf3 MINOR: mux-quic: Set tha SE abort reason when a STOP_SENDING frame is received
When STOP_SENDING frame is received for a quic stream, the error code is now
saved in the SE abort reason. To do so, we use the QUIC source
(SE_ABRT_SRC_MUX_QUIC). For now, this code is only set but not used on the
opposite side.
2024-05-06 22:00:00 +02:00
Christopher Faulet
20b156ee15 MEDIUM: mux-h2: Forward h2 client cancellations to h2 servers
When a H2 client sends a RST_STREAM(CANCEL) frame to abort a request, the
abort reason is now used on server side, in the H2 mux, to set the
RST_STREAM code. The main use case is to forward client cancellations to
gRPC applications.

This patch should fix the issue #172.
2024-05-06 22:00:00 +02:00
Christopher Faulet
dea79f3fe1 MINOR: mux-h2: Set the SE abort reason when a RST_STREAM frame is received
When RST_STREAM frame is received, the error code is now saved in the SE
abort reason. To do so, we use the H2 source (SE_ABRT_SRC_MUX_H2). For now,
this code is only set but not used on the opposite side.
2024-05-06 22:00:00 +02:00
Christopher Faulet
96f8b7ad08 MEDIUM: stconn/muxes: Add an abort reason for SE shutdowns on muxes
A reason is now passed as parameter to muxes shutdowns to pass additional
info about the abort, if any. No info means no abort or only generic one.

For now, the reason is composed of 2 32-bits integer. The first on represents
the abort code and the other one represents the info about the code (for
instance the source). The code should be interpreted according to the associated
info.

One info is the source, encoding on 5 bits. Other bits are reserverd for now.
For now, the muxes are the only supported source. But we can imagine to extend
it to applets, streams, health-checks...

The current design is quite simple and will most probably evolved.. But the
idea is to let the opposite side forward some errors and let's a mux know
why its stream was aborted. At first glance, a abort reason must only be
evaluated if SE_SHW_SILENT flag is set.

The main goal at short term, is to forward some H2 RST_STREAM codes because
it is mandatory for gRPC applications, mainly to forward gRPC cancellation
from an H2 client to an H2 server. But we can imagine to alter this reason
at the applicative level to enrich it. It would also be used to report more
accurate errors in logs.
2024-05-06 22:00:00 +02:00
Patrick Hemmer
28489021b3 BUG/MINOR: cfgparse: use curproxy global var from config post validation
Previously check_config_validity() had its own curproxy variable. This
resulted in the acl() sample fetch being unable to determine which
proxy was in use when used from within log-format statements. This
change addresses the issue by having the check_config_validity()
function use the global variable instead.
2024-05-06 18:45:47 +02:00
Patrick Hemmer
93d4e99714 BUG/MINOR: acl: support built-in ACLs with acl() sample
Built-in ACLs were not being searched by the acl() sample fetch. This
fixes that so they are searched if no other match is found.
2024-05-06 18:42:54 +02:00
Valentine Krasnobaeva
4a9e3e102e BUG/MINOR: haproxy: only tid 0 must not sleep if got signal
This patch fixes the commit eea152ee68
("BUG/MINOR: signals/poller: ensure wakeup from signals").

There is some probability that run_poll_loop() becomes inifinite, if
TH_FL_SLEEPING is withdrawn from all threads in the second signal_queue_len
check, when a signal has received just after the first one.

In such particular case, the 'wake' variable, which is used to terminate
thread's poll loop is never reset to 0. So, we never enter to the "stopping"
part of the run_poll_loop() and threads, except the one with id 0 (tid 0
handles signals), will continue to call _do_poll() eternally and will never
sleep, as its TH_FL_SLEEPING flag was unset.

This flag needs to be removed only for the tid 0, as it was done in the first
signal_queue_len check.

This fixes an issue #2537 "infinite loop when shutting down".

This fix must be backported in every stable version.
2024-05-06 18:39:08 +02:00
Aurelien DARRAGON
03ca16f38b OPTIM: log: resolve logformat options during postparsing
In lf_buildctx_prepare(), we perform costly bitwise operations for every
nodes to resolve node options and check for incompatibilities with global
options.

In fact, all this logic may safely be performed during postparsing. This
is what we're doing in this commit. Doing so saves us from unnecessary
runtime checks and could help speedup sess_build_logline().

Since checks are not as costly as before (due to them being performed
during postparsing and not on log building path anymore), an complementary
check for OPT_HTTP vs OPT_ENCODE incompatibity was added:

  encoding is ignored if HTTP option is set, unless HTTP option wasn't
  set globally and encoding was set globally, which means encoding
  takes the precedence

Thanks to this patch, lf_buildctx_prepare() now only takes care of
assigning proper typecast and options settings depending if it's used
from global or per-node context, and prepares CBOR-specific structure
members when CBOR encode option is set.
2024-05-06 11:13:46 +02:00
Ilia Shipitsin
a7cf2454dd BUILD: clock: improve check for pthread_getcpuclockid()
if _POSIX_THREAD_CPUTIME is greater than 0, pthread_getcpuclockid()
is implemented.

This should fix the build on Solaris 11.

Reference: https://docs.oracle.com/cd/E88353_01/html/E37842/unistd-3head.html
ML: https://www.mail-archive.com/haproxy@formilux.org/msg44915.html
2024-05-06 08:25:17 +02:00
Aurelien DARRAGON
d26a160133 OPTIM: log: speedup date printing in sess_build_logline() when no encoding is used
In sess_build_logline(), we have multiple fieds such as '%t' that build
a fixed-length string out of a date struct and then print it using
lf_rawtext(). In fact, printing it using lf_rawtext() is only mandatory
to deal with encoding options, but when no encoding is used we can output
the result to tmplog directly. Since most dates generate between 25 and 30
chars, doing so spares us from writing them twice and could help make
sess_build_logline() a bit faster when no encoding is used. (to match with
pre-encoding patch series performance).
2024-05-04 10:13:05 +02:00
Aurelien DARRAGON
bf3b4001ce OPTIM: log: use lf_buildctx's buffer instead of temporary stack buffers
Now that lf_buildctx isn't pushed on the stack anymore, let's take this
opportunity to store a small buffer of 256 bytes within it, and then use
this buffer as general purpose buffer to build fixed-length strings that
are then printed using lf_{raw}text() function. By doing so we stop
relying on temporary stack buffers.
2024-05-04 10:13:05 +02:00
Aurelien DARRAGON
ccc4341258 OPTIM: log: use thread local lf_buildctx to stop pushing it on the stack
Following previous commit's logic, let's move lf_buildctx ctx away from
sess_build_logline() to stop abusing from the stack to push large
structure each time sess_build_logline() is called. Also, don't memset
the structure for each invokation, but only reset members explicitly when
required.

For that we now declare one static lf_buildctx per thread (using
THREAD_LOCAL) and make sess_build_logline() refer to it using a pointer.
2024-05-04 10:13:05 +02:00
Aurelien DARRAGON
728b5aa835 OPTIM: log: declare empty buffer as global variable
'empty' buffer used in sess_build_logline() inside a loop, and since it
is only being read from and not modified, until recently it ended up being
cached most of the time and didn't cause overhead due to systematic push
on the stack.

However, due recent encoding work and new added variables on the stack,
we're starting to reach a stack limit and declaring 'empty' buffer within
the loop seems to cause non-negligible CPU overhead.

Since the variable isn't modified during log generation, let's declare
'empty' buffer as a global variable outside from sess_build_logline()
to prevent pushing it on the stack for each node evaluation.
2024-05-04 10:13:05 +02:00
Aurelien DARRAGON
cc2e94a948 BUG/MINOR: log: prevent double spaces emission in sess_build_logline()
Christian reported in GH #2556 that since 3.0-dev double spaces may be
found in log messages on some cases where it was not the case before.

As we were able to easily reproduce, a quick bisect led us to c6a7138
("MINOR: log: simplify last_isspace in sess_build_logline()"). While
it is true that all switch cases set the last_isspace variable to 0,
there was a subtelty for some fields such as '%hr', '%hrl', '%hs' or
'%hsl' and I overlooked it. Indeed, for '%hr', last_isspace was only set
to 0 if data was emitted, else the assignment didn't occur.

But with c6a7138, last_isspace is always set to 0 as long as the current
node type is not a separator. Because of that, if no data is emitted for
the current node value, and a space was already emitted prior to the
current node, then an extra space could be emitted after the node,
resulting in two spaces being emitted.

Note that while c6a7138 introduces a slight behavior regression regarding
last_isspace logic with the specific fields mentionned above, this
behavior could already be triggered with a failing or empty logformat
node sample expression. Consider this logformat expression:

  log-format "%{-M}o | %[str()] |"

str() will not print anything, and since we disabled mandatory option with
'-M', nothing gets printed for the node sample expression. As a result, we
have the following output:

  "|  |"

Instead of (when mandatory option is enabled):

  "| - |"

Thus in order to stick to the historical behavior, systematically set
last_isspace to 0 for EXPR nodes, and only set last_isspace to 0 when
data was written for TAG nodes. This way, '%hr', '%hrl', '%hs' or
'%hsl' should behave as before.

No backport needed.
2024-05-03 16:48:21 +02:00
Aurelien DARRAGON
48e0efb00b MEDIUM: log: optimizing tmp->type handling in sess_build_logline()
Instead of chaining 2 switchcases and performing encoding checks for all
nodes let's actually split the logic in 2: first handle simple node types
(text/separator), and then handle dynamic node types (tag, expr). Encoding
options are only evaluated for dynamic node types.

Also, last_isspace is always set to 0 after next_fmt label, since next_fmt
label is only used for dynamic nodes, thus != LOG_FMT_SEPARATOR.

Since LF_NODE_WITH_OPT() macro (which was introduced recently) is now
unused, let's get rid of it.

No functional change should be expected.

(Use diff -w to check patch changes since reindentation makes the patch
look heavy, but in fact it remains fairly small)
2024-05-03 16:48:21 +02:00
Ilia Shipitsin
a65c6d3574 CLEANUP: assorted typo fixes in the code and comments
This is 42nd iteration of typo fixes
2024-05-03 09:01:36 +02:00
Amaury Denoyelle
53782b9ea5 MINOR: stats: extract proxy clear-counter in a dedicated function
Split code related to proxies list looping in cli_parse_clear_counters()
to a new dedicated function. This function is placed in the new module
stats-proxy.
2024-05-02 16:43:26 +02:00
Amaury Denoyelle
f0644d1bd7 REORG: stats: define stats-proxy source module
Create a new module stats-proxy. Move stats functions related to proxies
list looping in it. This allows to reduce stats source file dividing its
size by half.
2024-05-02 16:42:36 +02:00
William Lallemand
271def959c MINOR: ssl: rename ocsp_update.http_proxy into ocsp-update.httpproxy
Rename to the option to have a more consistent name.
2024-05-02 16:32:06 +02:00
William Lallemand
964f093504 CLEANUP: ssl: rename new_ckch_store_load_files_path() to ckch_store_new_load_files_path()
Rename the new_ckch_store_load_files_path() function to
ckch_store_new_load_files_path(), in order to be more consistent.
2024-05-02 16:03:20 +02:00
Amaury Denoyelle
10ab56831e MINOR: stats: convert age as generic column for proxy stat
Convert FN_AGE in stat_cols_px[] as generic columns. These values will
be automatically used for dump/preload of a stats-file.

Remove srv_lastsession() / be_lastsession() function which are now
useless as last_sess is calculated via me_generate_field().
2024-05-02 10:55:25 +02:00
Amaury Denoyelle
e92ae8f0ba MINOR: stats: support age in stats-file
Extend generic stat column support to be able to fully support age stats
type. Several changes were required.

On output, me_generate_field() has been updated to report the difference
between the current tick with the stored value for FN_AGE type. Also, if
an age stats is hidden in show stats, -1 is returned instead of an empty
metric, which is the value to mark an age as unset.

On counters preload, load_ctr() was updated to handled FN_AGE. A similar
substraction is performed to the current tick value.
2024-05-02 10:55:25 +02:00
Amaury Denoyelle
634cc2a5d8 MINOR: counters: move last_change into counters struct
last_change was a member present in both proxy and server struct. It is
used as an age statistics to report the last update of the object.

Move last_change into fe_counters/be_counters. This is necessary to be
able to manipulate it through generic stat column and report it into
stats-file.

Note that there is a change for proxy structure with now 2 different
last_change values, on frontend and backend side. Special care was taken
to ensure that the value is initialized only on the proxy side. The
other value is set to 0 unless a listen proxy is instantiated. For the
moment, only backend counter is reported in stats. However, with now two
distinct values, stats could be extended to report it on both side.
2024-05-02 10:55:25 +02:00
Amaury Denoyelle
9b35e1f30c MINOR: stats: convert rate as generic column for proxy stats
Convert every FN_RATE in stat_cols_px[] to generic column. Thanks to
prior patch, this allows to automatically dump their value into
stats-file and preload corresponding freq-ctr on process startup.
2024-05-02 10:55:25 +02:00
Amaury Denoyelle
fec2ae9b76 MINOR: stats: support rate in stats-file
Implement support for FN_RATE stat column into stat-file.

For the output part, only minimal change is required. Reuse the function
read_freq_ctr() to print the same value in both stats output and
stats-file dump.

For counter preloading, define a new utility function
preload_freq_ctr(). This can be used to initialize a freq-ctr type by
preloading previous period value. Reuse this function in load_ctr()
during stats-file parsing.

At the moment, no rate column is defined as generic. Thus, this commit
does not have functional change. This will be changed as soon as FN_RATE
are converted to generic columns.
2024-05-02 10:55:25 +02:00
Amaury Denoyelle
639e73f8f2 MINOR: counters: move freq-ctr from proxy/server into counters struct
Move freq-ctr defined in proxy or server structures into their dedicated
fe_counters/be_counters struct.

Functionnaly no change here. This commit will allow to convert rate
stats column to generic one, which is mandatory to manipulate them in
the stats-file.
2024-05-02 10:55:25 +02:00
Amaury Denoyelle
4e9e841878 MINOR: stats: prepare stats-file support for values other than FN_COUNTER
Currently, only FN_COUNTER are dumped and preloaded via a stats-file.
Thus in several places we relied on the assumption that only FN_COUNTER
are valid in stats-file context.

New stats types will soon be implemented as they are also eligilible to
statistics reloading on process startup. Thus, prepare stats-file
functions to remove any FN_COUNTER restriction.

As one of this change, generate_stat_tree() now uses stcol_is_generic()
for stats name tree indexing before stats-file parsing.

Also related to stats-file parsing, individual counter preloading step
as been extracted from line parsing in a dedicated new function
load_ctr(). This will allow to extend it to support multiple mechanism
of counter preloading depending on the stats type.
2024-05-02 10:55:25 +02:00
Amaury Denoyelle
933b4ae27d MINOR: stats: convert req_tot as generic column
req_tot counter is a special case as it is not managed identically
between frontend and backend side.

For the backend side, this metric is available directly into
be_counters, which allows to use a generic stat column definition.

On the frontend side however, the metric value is an aggredate of
multiple fe_counters value. This is the case since the splitting between
HTTP version introduced in the following patch :

  9969adbcdc
  MINOR: stats: add by HTTP version cumulated number of sessions and requests

This difference cannot be handled automatically by me_generate_field().
Add a special case in the function to produce it on frontend side
reusing the aggregated value. This not done however for stats-file as
there is no counter to preload.
2024-05-02 10:55:25 +02:00
Amaury Denoyelle
56e6c57aa1 MINOR: stats: fix visual alignment for stat_cols_px definition
Simply adjust visual alignment in definition of proxy stats columns
definition for ST_I_PX_HANAFAIL column.
2024-05-02 10:55:25 +02:00
William Lallemand
3a19698b81 CLEANUP: ssl: move the global ocsp-update options parsing to ssl_ocsp.c
Move the global tunel.ssl.ocsp-update option parsing to ssl_ocsp.c.
2024-05-02 10:48:05 +02:00
William Lallemand
622c635815 CLEANUP: ssl: clean the includes in ssl_ocsp.c
Clean the includes in ssl_ocsp.c which were copied from ssl_sock.c and
are not relevant anymore.

Also move the include in the right order.
2024-05-02 10:35:27 +02:00
Valentine Krasnobaeva
5cbb278fae MINOR: capabilities: add cap_sys_admin support
If 'namespace' keyword is used in the backend server settings or/and in the
bind string, it means that haproxy process will call setns() to change its
default namespace to the configured one and then, it will create a
socket in this new namespace. setns() syscall requires CAP_SYS_ADMIN
capability in the process Effective set (see man 2 setns). Otherwise, the
process must be run as root.

To avoid to run haproxy as root, let's add cap_sys_admin capability in the
same way as we already added the support for some other network capabilities.

As CAP_SYS_ADMIN belongs to CAP_SYS_* capabilities type, let's add a separate
flag LSTCHK_SYSADM for it. This flag is set, if the 'namespace' keyword was
found during configuration parsing. The flag may be unset only in
prepare_caps_for_setuid() or in prepare_caps_from_permitted_set(), which
inspect process EUID/RUID and Effective and Permitted capabilities sets.

If system doesn't support Linux capabilities or 'cap_sys_admin' was not set
in 'setcap', but 'namespace' keyword is presented in the configuration, we
keep the previous strict behaviour. Process, that has changed uid to the
non-priviledged user, will terminate with alert. This alert invites the user
to recheck its configuration.

In the case, when haproxy will start and run under a non-root user and
'cap_sys_admin' is not set, but 'namespace' keyword is presented, this patch
does not change previous behaviour as well. We'll still let the user to try
its configuration, but we inform via warning, that unexpected things, like
socket creation errors, may occur.
2024-04-30 21:40:17 +02:00
Valentine Krasnobaeva
13ef552488 MINOR: sock: add EPERM case in sock_handle_system_err
setns() may return EPERM if thread, that tries to move into different
namespace, do not have CAP_SYS_ADMIN capability in its Effective set.
So, extending sock_handle_system_err() with this error allows to send
appropriate log message and set SF_ERR_PRXCOND (SC termination
flag in log) as stream termination error code. This error code can be
simply checked with SF_ERR_MASK at protocol layer.
2024-04-30 21:39:32 +02:00
Valentine Krasnobaeva
d3fc982cd7 MEDIUM: proto: make common fd checks in sock_create_server_socket
quic_connect_server(), tcp_connect_server(), uxst_connect_server() duplicate
same code to check different ERRNOs, that socket() and setns() may return.
They also duplicate some runtime condition checks, applied to the obtained
server socket fd.

So, in order to remove these duplications and to improve code readability,
let's encapsulate socket() and setns() ERRNOs handling in
sock_handle_system_err(). It must be called just before fd's runtime condition
checks, which we also move in sock_create_server_socket by the same reason.
2024-04-30 21:39:24 +02:00
Valentine Krasnobaeva
772d070ab5 MINOR: sock_set_mark: take sock family in account
SO_MARK, SO_USER_COOKIE, SO_RTABLE socket options (used to set the special
mark/ID on socket, in order to perform mark-based routing) are only supported
by AF_INET sockets. So, let's check socket address family, when we enter into
this function.
2024-04-30 21:38:29 +02:00
Valentine Krasnobaeva
d602d568e0 MEIDUM: unix sock: use my_socketat to create bind socket
As UNIX Domain sockets could be attached to Linux namespaces (see more details
about it from the Linux kernel patch set below:

	https://lore.kernel.org/netdev/m1hbl7hxo3.fsf@fess.ebiederm.org),

it is better to use my_socket_at() in order to create UNIX listener's socket.
my_socket_at() takes in account a network namespace, that may be configured
for a frontend in the bind line:

	frontend fe_foo
		...
		bind uxst@frontend.sock user haproxy group haproxy mode 660 namespace frontend

Like this, namespace aware applications as netstat for example, will see this
listening socket in its 'frontend' namespace and not in the root namespace as
it was before.

It is important to mention, that fixes in Linux kernel referenced above allow
to connect to this listener's socket from the root and from any other
namespace. UNIX Domain socket is protected by its permission set, which must
be set with caution on its inode.
2024-04-30 21:38:24 +02:00
Valentine Krasnobaeva
84babc93ce MEDIUM: proto_uxst: take in account server namespace
As UNIX Domain sockets could be attached to Linux namespaces (see more details
about it from the Linux kernel patch set below:

	https://lore.kernel.org/netdev/m1hbl7hxo3.fsf@fess.ebiederm.org),

it is better to use sock_create_server_socket() in UNIX stream protocol
implementation, as this function calls my_socket_at() and the latter takes
in account server network namespace, which could be configured as in example
below:

       backend be_bar
                ...
                server rpicam0 /run/ustreamer.sock namespace foonet

So, for UNIX Domain socket, used as an address of some backend server, this
patch makes possible to perform connect() to this backend server from the same
network namespace, where the server is running, or where its listening socket
was created.

Using sock_create_server_socket() in UNIX stream protocol implementation also
makes the code of uxst_connect_server() more uniform with tcp_connect_server()
and quic_connect_server().
2024-04-30 21:38:18 +02:00
Valentine Krasnobaeva
a0b5324cff MINOR: sock: rename sock to sock_fd in sock_create_server_socket
Renaming sock to sock_fd makes it more clear, that sock_create_server_socket
returns the fd of newly created server socket and then we check this fd.
As we heavily use "fd" variable name in all protocol implementations, let's
prefix this one with the name of its object file: sock.o.
2024-04-30 21:38:12 +02:00
Willy Tarreau
072686dafd BUG/MINOR: stconn: don't wake up an applet waiting on buffer allocation
Since the extension of the buffers API to applets in 3.0-dev, an applet
may find itself unable to allocate a buffer, and will block respectively
on APPCTX_FL_OUTBLK_ALLOC or APPCTX_FL_INBLK_ALLOC depending on the
direction. However the code in sc_applet_process() doesn't consider this
situation when deciding to wake up an applet, so when the condition
arises, the applet keeps ringing and is killed by the loop detector.

The fix is trivial and simply consists in checking for the flags above.
No backport is needed since this is new in 3.0.
2024-04-30 21:36:47 +02:00
Aurelien DARRAGON
12d08cf912 BUG/MEDIUM: log: don't ignore disabled node's options
In 3f2e8d0ed ("MEDIUM: log: lf_* build helpers now take a ctx argument")
I made a mistake, because starting with this commit it is no longer
possible from a node to disable global logformat options.
The result is that when an option is set globally, it cannot be disabled
anymore.

For instance, it is not possible to do this anymore:
  log-format "%{+X}o %{-X}Ts"

The original intent was to prevent encoding options from being
disabled once enabled globally, because when encoding is enabled globally
we start the object enumeration right away (ie: in CBOR and JSON we
announce dynamic map, and for each node we announce the key..), thus it
doesn't make sense to mix encoding types there, unless encoding is only
used per-node, in which case only the value gets encoded, thus it remains
possible to print a value in JSON/CBOR-compatible format while the next
one shouldn't be printed as-is.

Thus, to restore the original behavior, slightly change the logic in
lf_buildctx_prepare() so that only global encoding options take the
precedence over node's options (instead of all options).

No backport needed.
2024-04-30 18:45:07 +02:00
Aurelien DARRAGON
41d7e82e0f MINOR: log/cbor: _lf_cbor_encode_byte() explicitly requires non-NULL ctx (again)
The BUG_ON() statement that was added in 9bdea51 ("MINOR: log/cbor:
_lf_cbor_encode_byte() explicitly requires non-NULL ctx") isn't
sufficient as Coverity still thinks the lf_buildctx itself may be NULL
as shown in GH #2554. In fact the original reports complains about the
lf_buildctx itself and I didn't understand it properly, let's add another
check in the BUG_ON() to ensure both cbor_ctx and cbor_ctx->ctx are not
NULL since it is not expected if used properly.
2024-04-30 10:10:35 +02:00
Aurelien DARRAGON
9931a62c3f BUG/MINOR: log: fix global lf_expr node options behavior (2nd try)
In 98b44e8 ("BUG/MINOR: log: fix global lf_expr node options behavior"),
I properly restored global node options behavior for when encoding is
not used, however the fix is not optimal when encoding is involved:

Indeed, encoding logic in sess_build_logline() relies on global node
options to know if encoding must be handled expression-wide or
individually. However, because of the above fix, if an expression is
made of 1 or multiple nodes that all set an encoding option manually
(without '%o'), we consider that the option was set globally, but
that's probably not what the user intended. Instead we should only
evaluate global options from '%o', so that it remains possible to
skip global encoding when needed.

No backport needed.
2024-04-30 10:10:35 +02:00
Aurelien DARRAGON
97240d01b3 BUG/MINOR: log/encode: fix potential NULL-dereference in LOGCHAR()
When CBOR encoding was added in c614fd3b9 ("MINOR: log: add +cbor encoding
option"), in LOGCHAR(), we forgot to check that we don't assign the NULL
value to tmplog (as we assume that tmplog cannot be NULL at the end of
sess_build_logline())

No backport needed.
2024-04-30 10:10:35 +02:00
Aurelien DARRAGON
949ac95aa6 BUG/MINOR: log/encode: consider global options for key encoding
In sess_build_logline(), contrary to what's stated in the comment
"only consider global ctx for key encoding", we check for
LOG_OPT_ENCODE flag on the current ctx options instead of global
ones. Because of this, we could end up doing the wrong thing if the
previous node had encoding enabled but it isn't set globally for
instance.

To fix the issue, let's simply check the presence of the flag on
g_options before entering the "key encoding" block.

This bug was introduced with 3f7c8387 ("MINOR: log: add +json encoding
option"), no backport needed.
2024-04-30 10:10:35 +02:00
William Lallemand
6b634c4779 MINOR: ssl: introduce ocsp_update.http_proxy for ocsp-update keyword
The ocsp_update.http_proxy global option allows to set an HTTP proxy
address which will be used to send the OCSP update request with an
absolute form URI.
2024-04-29 17:23:02 +02:00
William Lallemand
95949e6868 MINOR: httpclient: allow to use absolute URI with new flag HC_F_HTTPROXY
The new HC_F_HTTPPROXY flag allows to use an absolute URI within a
request that won't be modified in order to use an http proxy.
2024-04-29 17:10:47 +02:00
Aurelien DARRAGON
9bdce67585 CLEANUP: log: add a macro to know if a lf_node is configurable
LF_NODE_WITH_OPT(node) returns true if the node's option may be set and
thus should be considered. Logic is based on logformat node's type:
for now only TAG and FMT nodes can be configured.
2024-04-29 14:47:37 +02:00
Aurelien DARRAGON
98b44e8edb BUG/MINOR: log: fix global lf_expr node options behavior
In 507223d5 ("MINOR: log: global lf_expr node options"), a mistake was
made because it was assumed that only the last occurence of %o
(LOG_FMT_GLOBAL) should be kept as global node options.

However, although not documented, it is possible to have multiple %o
within a single logformat expression to change the global settings on the
fly.

For instance, consider this example:

  log-format "%{+X}o test1=%ms %{-X}o test2=%ms %{+X}o test3=%ms"

Prior to 3f2e8d0ed ("MEDIUM: log: lf_* build helpers now take a ctx
argument"), this would output something like this:

  test1=18B test2=395 test3=18B

This is because global options is properly updated as the lf_expr string
is parsed. But now due to 507223d5 and 3f2e8d0ed, only the last %o
occurence is considered. With the above example, this gives:

  test1=18B test2=18B test3=18B

To restore historical behavior, let's partially revert 507223d5: to
compute global node options, we now start with all options enabled and
then for each configurable node in lf_expr_postcheck(), we keep options
common to the current node and previous nodes using AND masking, this way
we really end up with options common to all nodes.

No backport needed.
2024-04-29 14:47:37 +02:00
Aurelien DARRAGON
9bdea51d7e MINOR: log/cbor: _lf_cbor_encode_byte() explicitly requires non-NULL ctx
As shown in GH #2550, Coverity is tempted to think that NULL-dereference
can occur in _lf_cbor_encode_byte() due to user-ctx being dereferenced
from cbor_ctx, while coverity thinks that cbor_ctx may be NULL.

In practise this cannot happen, because _lf_cbor_encode_byte() is
only leveraged through a function pointer that is set in conjunction with
the function pointer ctx (which ain't NULL). All this logic is done inside
lf_buildctx_prepare() when LOG_OPT_ENCODE_CBOR is set.

Since coverity doesn't seem to understand the logic properly, then it
might as well confuse humans, so let's make it clear in
_lf_cbor_encode_byte() that we expect non-NULL ctx by adding a BUG_ON()
2024-04-29 14:47:37 +02:00
Aurelien DARRAGON
0e2aea8224 CLEANUP: tools/cbor: rename cbor_encode_ctx struct members
Rename e_byte_fct to e_fct_byte and e_fct_byte_ctx to e_fct_ctx, and
adjust some comments to make it clear that e_fct_ctx is here to provide
additional user-ctx to the custom cbor encode function pointers.

For now, only e_fct_byte function may be provided, but we could imagine
having e_fct_int{16,32,64}() one day to speed up the encoding when we
know we can encode multiple bytes at a time, but for now it's not worth
the hassle.
2024-04-29 14:47:37 +02:00
Amaury Denoyelle
20bc42e697 BUG/MINOR: stats: replace objt_* by __objt_* macros
Update parse_stat_line() used during stats-file parsing. For each line,
GUID is extracted first to access to the object instance. obj_type()
is then invoked to retrieve the correct object type.

Replace objt_* by __objt_* macros to mark its result as safe and non
NULL.

This should fix coverity report from github issue #2550.

No need to backport.
2024-04-29 14:21:10 +02:00
Remi Tricot-Le Breton
0610f52bcd BUG/MEDIUM: cache: Vary not working properly on anything other than accept-encoding
If a response varies on anything other than accept-encoding (origin or
referer) but still contains an 'Encoding' header, the cached responses
were never sent back.
This is because of the 'set_secondary_key_encoding' call that always
filled the accept-encoding part of the secondary signature with the
response's actual encoding, regardless of whether the response varies on
this or not. This meant that the accept-encoding part of the signature
could be non-null in the cached entry which made the
'get_secondary_entry' calls in 'http_action_req_cache_use' always fail
because in those cases the request's secondary signature always had a
null accept-encoding part.

This patch can be backported up to branch 2.4.
2024-04-29 10:41:46 +02:00
Willy Tarreau
b957e741b0 MINOR: cli/wait: rename the condition "srv-unused" to "srv-removable"
As previously discussed, "srv-unused" is sufficiently ambiguous to cause
some trouble over the long term. Better use "srv-removable" to indicate
that the server is removable, and if the conditions to delete a server
change over time, the wait condition will be adjusted without renaming
it.
2024-04-27 09:36:36 +02:00
Willy Tarreau
bc236ad133 CLEANUP: dynbuf: move the reserve and limit parsers to dynbuf.c
I just added a new setting to set the number of reserved buffer, to
discover we already had one... Let's move the parsing of this keyword
(tune.buffers.reserve) and tune.buffers.limit to dynbuf.c where they
should be.
2024-04-27 09:36:36 +02:00
Aurelien DARRAGON
c33b857df9 MINOR: log: support true cbor binary encoding
CBOR in hex format as implemented in previous commit is convenient because
the produced output is portable and can easily be embedded in regular
syslog payloads.

However, one of the goal of CBOR implementation is to be able to produce
"Concise Binary" object representation. Here is an excerpt from cbor.io
website:

  "Some applications also benefit from CBOR itself being encoded in
   binary. This saves bulk and allows faster processing."

Currently we don't offer that with '+cbor', quite the opposite actually
since a text string encoded with '+cbor' option will be larger than a
text string encoded with '+json' or without encoding at all, because for
each CBOR binary byte, 2 characters will be emitted.

Hopefully, the sink/log API allows for binary data to be passed as
parameter, this is because all relevant functions in the chain don't rely
on the terminating NULL byte and take a string pointer + string length as
parameter. We can actually rely on this property to support the '+bin'
option when combined with '+cbor' to produce RAW binary CBOR output.
Be careful though, as this is only intended for use with set-var-fmt or to
send binary data to capable UDP/ring endpoints.

Example:
  log-format "%{+cbor,+bin}o %(test)[bin(00AABB)]"

Will produce:
  bf64746573745f4300aabbffff

(output was piped to `hexdump  -ve '1/1 "%.2x"'` to dump raw bytes as HEX
characters)

With cbor.me pretty printer, it gives us:
  BF              # map(*)
     64           # text(4)
        74657374  # "test"
     5F           # bytes(*)
        43        # bytes(3)
           00AABB # "\u0000\xAA\xBB"
        FF        # primitive(*)
     FF           # primitive(*)
2024-04-26 18:39:32 +02:00
Aurelien DARRAGON
c614fd3b9f MINOR: log: add +cbor encoding option
In this patch, we make use of the CBOR (RFC8949) encode helper functions
from the previous commit to implement '+cbor' encoding option for log-
formats. The logic behind it is pretty similar to '+json' encoding option,
except that the produced output is a CBOR payload written in HEX format so
that it remains compatible to use this with regular syslog endpoints.

Example:
  log-format "%{+cbor}o %[int(4)] test %(named_field)[str(ok)]"

Will produce:
  BF6B6E616D65645F6669656C64626F6BFF

  Detailed view (from cbor.me):
    BF                           # map(*)
       6B                        # text(11)
          6E616D65645F6669656C64 # "named_field"
       62                        # text(2)
          6F6B                   # "ok"
       FF                        # primitive(*)

If the option isn't set globally, but on a specific node instead, then
only the value will be encoded according to CBOR specification.

Example:
  log-format "test cbor bool: %{+cbor}[bool(true)]"

Will produce:
  test cbor bool: F5
2024-04-26 18:39:32 +02:00
Aurelien DARRAGON
810303e3e6 MINOR: tools: add cbor encode helpers
Add cbor helpers to encode strings (bytes/text) and integers according to
RFC8949, also add cbor_encode_ctx struct to pass encoding options such as
how to encode a single byte.
2024-04-26 18:39:32 +02:00
Aurelien DARRAGON
3f7c8387c0 MINOR: log: add +json encoding option
In this patch, we add the "+json" log format option that can be set
globally or per log format node.

What it does, it that it sets the LOG_OPT_ENCODE_JSON flag for the
current context which is provided to all lf_* log building function.

This way, all lf_* are now aware of this option and try to comply with
JSON specification when the option is set.

If the option is set globally, then sess_build_logline() will produce a
map-like object with key=val pairs for named logformat nodes.
(logformat nodes that don't have a name are simply ignored).

Example:
  log-format "%{+json}o %[int(4)] test %(named_field)[str(ok)]"

Will produce:
  {"named_field": "ok"}

If the option isn't set globally, but on a specific node instead, then
only the value will be encoded according to JSON specification.

Example:
  log-format "{ \"manual_key\": %(named_field){+json}[bool(true)] }"

Will produce:
  {"manual_key": true}

When the option is set, +E option will be ignored, and partial numerical
values (ie: because of logasap) will be encoded as-is.
2024-04-26 18:39:32 +02:00
Aurelien DARRAGON
b7c3d8c87c MINOR: log: add +bin logformat node option
Support '+bin' option argument on logformat nodes to try to preserve
binary output type with binary sample expressions.

For this, we rely on the log/sink API which is capable of conveying binary
data since all related functions don't search for a terminating NULL byte
in provided log payload as they take a string pointer and a string length
as argument.

Example:
  log-format "%{+bin}o %[bin(00AABB)]"

Will produce:
  00aabb

(output was piped to `hexdump  -ve '1/1 "%.2x"'` to dump raw bytes as HEX
characters)

This should be used carefully, because many syslog endpoints don't expect
binary data (especially NULL bytes). This is mainly intended for use with
set-var-fmt actions or with ring/udp log endpoints that know how to deal
with such binary payloads.

Also, this option is only supported globally (for use with '%o'), it will
not have any effect when set on an individual node. (it makes no sense to
have binary data in the middle of log payload that was started without
binary data option)
2024-04-26 18:39:31 +02:00
Aurelien DARRAGON
162e311a0e MINOR: log: add no_escape_map to bypass escape with _lf_encode_bytes()
Providing no_escape_map as <map> argument to _lf_encode_bytes() function
will make the function skip escaping since the map is empty.

This is for convenience, as it might be useful to call lf_encode_chunk()
to encoding binary data without escaping it.
2024-04-26 18:39:31 +02:00
Aurelien DARRAGON
fb8b47fed8 MINOR: log: postpone conversion for sample expressions in sess_build_logline()
In sess_build_logline(), for sample expression nodes, instead of directly
calling sample_fetch_as_type(... SMP_T_STR), let's first process the
sample using sample_process(), and then proceed with the conversion to
str if required.

Doing so will allow us to implement type casting and preserving logic.
2024-04-26 18:39:31 +02:00
Aurelien DARRAGON
84963fb743 MINOR: log: expose node typecast in lf_buildctx struct
Store node->typecast setting inside lf_buildctx struct so that encoding
functions may benefit from it.
2024-04-26 18:39:31 +02:00
Aurelien DARRAGON
3f2e8d0ed2 MEDIUM: log: lf_* build helpers now take a ctx argument
Add internal lf_buildctx struct that is only used inside
sess_build_logline() scope and is passed to lf_* log building helpers
to expose current building context. For now, node options and the in_text
counter are stored in the ctx struct. Thanks to this change, lf_* building
functions don't depend on a logformat_node struct pointer, and may be used
in a standalone manner as long as a build context is provided.

Also, global options are now handled explictly in sess_build_logline() to
make sure that global options are always considered even if they were not
duplicated on every nodes.

No functional change should be expected.
2024-04-26 18:39:31 +02:00
Aurelien DARRAGON
f7cb384f1a MINOR: log: merge lf_encode_string() and lf_encode_chunk() logic
lf_encode_string() and lf_encode_chunk() function are pretty similar. The
only difference is the stopping behavior, encode_chunk stops at a given
position while encode_string stops when encountering '\0'. Moreover,
both functions leverage tools.c encode helpers, but because of the
LOG_OPT_ESC option, they reimplement those helpers with added logic.

Instead of having to deal with code duplication which makes both functions
harder to maintain, let's define a _lf_encode_bytes() helper function
which satisfies lf_encode_string() and lf_encode_chunk() needs while
keeping the function as simple as possible.

_lf_encode_bytes() itself is made of multiple static inline helper
functions, in the attempt to keep checks outside of core loop for
better performance.
2024-04-26 18:39:31 +02:00
Aurelien DARRAGON
a1583ec7c7 MINOR: log: make all lf_* sess build helper static
There is no need to expose such functions since they are only involved in
the log building process that occurs inside sess_build_logline().

Making functions static and removing their public prototype to ease code
maintenance.
2024-04-26 18:39:31 +02:00
Aurelien DARRAGON
3b9096bd36 MINOR: log: use LOG_VARTEXT_{START,END} to enclose text strings
Rename LOGQUOTE_{START,END} macros to more generic LOG_VARTEXT_{START,END}
in order to prepare for new encoding types that rely on specific treatment
for variable-length texts. No functional change should be expected.
2024-04-26 18:39:31 +02:00
Aurelien DARRAGON
278d6c3379 MINOR: log: explicitly handle %ts and %tsc as text strings
Build fixed-length strings for %ts and %tsc to be able to print them
using lf_rawtext_len(), this way it will be easier to encode them
when new encoding options will be added.

No functional change should be expected.
2024-04-26 18:39:31 +02:00
Aurelien DARRAGON
2e4cc517bf MEDIUM: log: use lf_rawtext for lf_ip() and lf_port() hex strings
Same as the previous commit, but for ip and port oriented values when
+X option is provided.

No functional change should be expected.

Because of this patch, we add a little overhead because we first generate
the text into a temporary variable and then use lf_rawtext() to print it.
Thus we have a double-copy, and this could have some performance
implications that were not yet evaluated. Due to the small number of bytes
that can end up being copied twice, we could be lucky and have no visible
performance impact, but if we happen to see a significant impact, it could
be useful to add a passthrough mechanism (to keep historical behavior)
when no encoding is involved.
2024-04-26 18:39:31 +02:00
Aurelien DARRAGON
3a3bdf1c76 MEDIUM: log: write raw strings using lf_rawtext()
Make use of the previous commit to print strings that should not be
modified.

For instance, when +X option is provided, we have to print numerical
values in ASCII HEX form. For that, we used snprintf() to output the
result to the log output buffer directly, but now we build the string in
a temporary buffer of fixed-size and then print it using lf_rawtext()
which will take care of encoding options.

Because of this patch, we add a little overhead because we first generate
the text into a temporary variable and then use lf_rawtext() to print it.
Thus we have a double-copy, and this could have some performance
implications that were not yet evaluated. Due to the small number of bytes
that can end up being copied twice, we could be lucky and have no visible
performance impact, but if we happen to see a significant impact, it could
be useful to add a passthrough mechanism (to keep historical behavior)
when no encoding is involved.
2024-04-26 18:39:31 +02:00
Aurelien DARRAGON
0d1e99c086 MEDIUM: log: pass date strings to lf_rawtext()
Don't directly call functions that take date as argument and output the
string representation to the log output buffer under sess_build_logline(),
and instead build the strings in temporary buffers of fixed size
(hopefully such functions, such as date2str_log() and gmt2str_log()
procuce strings of known size), and then print the result using
lf_rawtext() helper function. This way, we will be able to encode them
automatically as regular string/text when new encoding methods are added.

Because of this patch, we add a little overhead because we first generate
the text into a temporary variable and then use lf_rawtext() to print it.
Thus we have a double-copy, and this could have some performance
implications that were not yet evaluated. Due to the small number of bytes
that can end up being copied twice (< 30), we could be lucky and have no
visible performance impact, but if we happen to see a significant impact,
it could be useful to add a passthrough mechanism (to keep historical
behavior) when no encoding is involved.
2024-04-26 18:39:31 +02:00
Aurelien DARRAGON
fcb7e4beaa MINOR: log: add lf_rawtext{_len}() functions
similar to lf_text_{len}, except that quoting and mandatory options are
ignored. Use this to print the input string without any modification (
except for encoding logic).
2024-04-26 18:39:31 +02:00
Aurelien DARRAGON
1fa2da18cd MINOR: log: add lf_int() wrapper to print integers
Wrap ltoa(), lltoa(), ultoa() and utoa_pad() functions that are used by
sess_build_logline() to print numerical values by implementing a dedicated
helper named lf_int() that takes <dft_hld> as argument to know how to
write the integer by default (when no encoding is specified).

LF_INT_UTOA_PAD_4 is used to emulate utoa_pad(x, 4) since it's found only
once under sess_build_logline(), thus there is no need to pass an extra
parameter to lf_int() function.
2024-04-26 18:39:31 +02:00
Aurelien DARRAGON
d3c92a3a83 MINOR: log: skip custom logformat_node name if empty
Reminder:

Since 3.0-dev4, we can optionally give a name to logformat nodes:

  log-format "%(custom_name1)B %(custom_name2)[str(value)]"

But we may also optionally set the expected node type by appending
':type' after the name, type being either sint,str or bool, like this:

  log-format "%(string_as_int:sint)[str(14)]"

However, it is currently not possible to provide a type without providing
a name that is a least 1 char long. But it could be useful to provide a
type without setting a name, like this, for typecasting purposes only:

  log-format "%(:sint)[bool(true)]"

Thus in order to allow this usage, don't set node->name if node name is
not at least 1 character long. By doing so, node->name will remain NULL
and will not be considered, but the typecast setting will.
2024-04-26 18:39:31 +02:00
Aurelien DARRAGON
c584600083 CLEANUP: log: simplify complex values usages in sess_build_logline()
make sess_build_logline() switch case more readable by performing some
simplifications: complex values are first extracted in a temporary
variable so that it's easier to refer to them and at a single place.
2024-04-26 18:39:31 +02:00
Aurelien DARRAGON
507223d527 MINOR: log: global lf_expr node options
Add options to lf_expr->nodes to store global options (those that are
common to all node) for easier access.

No functional change should be expected.
2024-04-26 18:39:31 +02:00
Aurelien DARRAGON
7ff4f09e23 MINOR: log: store lf_expr nodes inside substruct
Add another struct level inside lf_expr struct to allow new information
to be stored alongside lf_expr nodes.
2024-04-26 18:39:31 +02:00
Aurelien DARRAGON
f8e1357a05 CLEANUP: log: remove unused checks for encode_{chunk,string}
Thanks to 8226e92eb ("BUG/MINOR: tools/log: invalid
encode_{chunk,string} usage"), we only need to check for NULL return
value from encode_{chunk,string}() and escape_string() to know if the
call failed.
2024-04-26 18:39:31 +02:00
William Lallemand
2ab42dddc4 BUG/MINOR: mworker: reintroduce way to disable seamless reload with -x /dev/null
Since the introduction of the automatic seamless reload using the
internal socketpair, there is no way of disabling the seamless reload.

Previously we just needed to remove -x from the startup command line,
and remove any "expose-fd" keyword on stats socket lines.

This was introduced in 2be557f7c ("MEDIUM: mworker: seamless reload use
the internal sockpairs").

The patch copy /dev/null again and pass it to the next exec so we never
try to get socket from the -x.

Must be backported as far as 2.6.
2024-04-26 15:25:49 +02:00
Amaury Denoyelle
e4a29447ce MEDIUM: stats: define stats-file keyword
This commit is the final to implement preloading of haproxy internal
counters via stats-file parsing.

Define a global keyword "stats-file". It allows to specify the path to
the stats-file which will be parsed on process startup.
2024-04-26 14:18:15 +02:00
Amaury Denoyelle
782be288ca MINOR: stats: parse values from stats-file
This patch implement parsing of counter values line from stats-file. It
reuses domain context previously set by the last header line. Each
value is separated by ',' character, relative to the list of column
names describe by the header line.

This is implemented via static function parse_stat_line(). It first
extract a GUID and retrieve the object instance. Then each numerical
value is parsed and object counters updated. For the moment, only U64
counters metrics is supported. parse_stat_line() is called on each line
until a new header line is found.
2024-04-26 11:34:02 +02:00
Amaury Denoyelle
374dc08611 MINOR: stats: parse header lines from stats-file
This patch implements parsing of headers line from stats-file.

A header line is defined as starting with '#' character. It is directly
followed by a domain name. For the moment, either 'fe' or 'be' is
allowed. The following lines will contain counters values relatives to
the domain context until the next header line.

This is implemented via static function parse_header_line(). It first
sets the domain context used during apply_stats_file(). A stats column
array is generated to contains the order on which column are stored.
This will be reused to parse following lines values.

If an invalid line is found and no header was parsed, considered the
stats-file as ill formatted and stop parsing. This allows to immediately
interrupt parsing if a garbage file was used without emitting a ton of
warnings to the user.
2024-04-26 11:34:02 +02:00
Amaury Denoyelle
34ae7755b3 MINOR: stats: apply stats-file on process startup
This commit is the first one of a serie to implement preloading of
haproxy counters via stats-file parsing.

This patch defines a basic apply_stats_file() function. It implements
reading line by line of a stats-file without any parsing for the moment.
It is called automatically on process startup via init().
2024-04-26 11:29:25 +02:00
Amaury Denoyelle
83731c8048 MINOR: guid: define guid_is_valid_fmt()
Extract GUID format validation in a dedicated function named
guid_is_valid_fmt(). For the moment, it is only used on guid_insert().

This will be reused when parsing stats-file, to ensure GUID has a valid
format before tree lookup.
2024-04-26 11:29:25 +02:00
Amaury Denoyelle
e74148fb7c MEDIUM: stats: implement dump stats-file CLI
Define a new CLI command "dump stats-file" with its handler
cli_parse_dump_stat_file(). It will loop twice on proxies_list to dump
first frontend and then backend side. It reuses the common function
stats_dump_stat_to_buffer(), using STAT_F_BOUND to restrict on the
correct side.

A new module stats-file.c is added to regroup function specifics to
stats-file. It defines two main functions :
* stats_dump_file_header() to generate the list of column list prefixed
  by the line context, either "#fe" or "#be"
* stats_dump_fields_file() to generate each stat lines. Object without
  GUID are skipped. Each stat entry is separated by a comma.

For the moment, stats-file does not support statistics modules. As such,
stats_dump_*_line() functions are updated to prevent looping over stats
module on stats-file output.
2024-04-26 10:20:57 +02:00
Amaury Denoyelle
83281303f6 MINOR: stats: define stats-file output format support
Prepare stats function to handle a new format labelled "stats-file". Its
purpose is to generate a statistics dump with a format closed from the
CSV output. Such output will be then used to preload haproxy internal
counters on process startup.

stats-file output differs from a standard CSV on several points. First,
only an excerpt of all statistics is outputted. All values that does not
make sense to preload are excluded. For the moment, stats-file only list
stats fully defined via "struct stat_col" method. Contrary to a CSV, sll
columns of a stats-file will be filled. As such, empty field value is
used to mark stats which should not be outputted.

Some adaptation specifics to stats-file are necessary into
me_generate_field(). First, stats-file will output separatedly values
from frontend and backend sides with their own respective set of
columns. As such, an empty field value is returned if stat is not
defined for either frontend/listener, or backend/server when outputting
the other side. Also, as stats-file does not support empty column,
stcol_hide() is not used for it.

A minor adjustement was necessary for stats_fill_fe_line() to pass
context flags. This is necessary to detect stat output format. All other
listener/server/backend corresponding functions already have it.
2024-04-26 10:20:57 +02:00
Amaury Denoyelle
6615252656 MEDIUM: stats: convert counters to new column definition
Convert most of proxy counters statistics to new "struct stat_col"
definition. Remove their corresponding switch..case entries in
stats_fill_*_line() functions. Their value are automatically calculate
via me_generate_field() invocation.

Along with this, also complete stcol_hide() when some stats should be
hidden.

Only a few counters where not converted. This is because they rely on
values stored outside of fe/be_counters structure, which
me_generate_field() cannot use for now.
2024-04-26 10:20:57 +02:00
Amaury Denoyelle
168301411d MINOR: stats: hide some columns in output
Metric style stats can be automatically calculate since the introduction
of metric_generate() when using "struct stat_col" as input. This would
allow to centralize statistics generation. However, some stats are not
outputted under specific condition. For example, health check failures
on a server are only reported if checks are active.

To support this, define a new function metric_hide(). It is called by
metric_generate(). If true, it will skip metric calcuation and return an
empty field value instead. This allows to define "stat_col" metrics and
calculate them with metric_generate() but hiding them under certain
circumstances.
2024-04-26 10:20:57 +02:00
Amaury Denoyelle
a7810b7be6 MINOR: stats: implement automatic metric generation from stat_col
This commit is a direct follow-up of the previous one which define a new
type "struct stat_col" to fully define a statistic entry.

Define a new function metric_generate(). For metrics statistics, it is
able to automatically calculate a stat value field for "offsets" from
"struct stat_col". Use it in stats_fill_*_stats() functions. Maintain a
fallback to previously used switch-case for old-style statistics.

This commit does not introduce functional change as currently no
statistic is defined as "struct stat_col". This will be the subject of a
future commit.
2024-04-26 10:20:57 +02:00
Amaury Denoyelle
65624876f2 MINOR: stats: introduce a more expressive stat definition method
Previously, statistics were simply defined as a list of name_desc, as
for example "stat_cols_px" for proxy stats. No notion of type was fixed
for each stat definition. This correspondance was done individually
inside stats_fill_*_line() functions. This renders the process to
define new statistics tedious.

Implement a more expressive stat definition method via a new API. A new
type "struct stat_col" for stat column to replace name_desc usage is
defined. It contains a field to store the stat nature and format. A
<cap> field is also defined to be able to define a proxy stat only for
certain type of objects.

This new type is also further extended to include counter offsets. This
allows to define a method to automatically generate a stat value field
from a "struct stat_col". This will be the subject of a future commit.

New type "struct stat_col" is fully compatible full name_desc. This
allows to gradually convert stats definition. The focus will be first
for proxies counters to implement statistics preservation on reload.
2024-04-26 10:20:57 +02:00
Amaury Denoyelle
861370a6d4 MINOR: stats: update ambiguous "metrics" naming to "stat_cols"
The name "metrics" was chosen to represent the various list of haproxy
exposed statistics. However, it is deemed as ambiguous as some stats are
indeed metric in the true sense, but some are not, as highlighted by
various "enum field_origin" values.

Replace it by the new name "stat_cols" for statistic columns. Along with
the already existing notion of stat lines it should better reflect its
purpose.
2024-04-26 10:20:57 +02:00
Christopher Faulet
4b1a7ea66c BUG/MINOR: peers: Don't wait for a remote resync if there no remote peer
When a resync is needed, a local resync is first tried and if it does not
work, a remote resync is tried. It happens when the worker is started for
instance. There is a timeout to wait for the local resync, except for the
first start. And if the local resync fails or times out, the same timeout
is applied to the remote resync. This one is always applied, even if there
is no remote peer.

On the other hand, on reload, if the old worker has never performed its
resync, it does not try to resync the new worker. And here there is an
issue. On the first reload, when there is no remote peer, we must wait for
the resync timeout expiration to have a chance to resync the new worker. If
the reload happens too early, there is no resync at all. Concretly, after a
fresh start, if a reload happens in the first 5 seconds, there is no resync
with the new worker. The issue only concerns the first reload and affects
the second worker.

To fix the issue, we must only skip the remote resync if there is no remote
peer. This way, on a fresh start, the worker is immediately considered as
resync. The local reynsc is skipped because it is the first worker and the
remote resync is skipped because there is no remote peer.

This patch must be backported to all stable versions.
2024-04-25 21:47:02 +02:00
Christopher Faulet
0243691de1 REORG: peers: Rename all occurrences to 'ps' variable
In loops on the peer list in the code, the 'ps' variable was used as a
shortcut for the peer session. However, if mays be confusing with the peers
section too. So, all occurrences to 'ps' variable were renamed to 'peer'.
2024-04-25 18:29:58 +02:00
Christopher Faulet
fff5f63e10 BUG/MEDIUM: peers: Use atomic operations on peers flags when necessary
Peers flags are mainly used from the sync task. At least, it is only updated
by the sync task. However, there is one place where a peer may read these
flags, when the message marking the end of a synchro is sent.

So to be sure the value retrieved at this place is consistent, we must use
an atomic operation to read it. And of course, from the sync task, atomic
operations must be used to update peers flags. However, from the sync task,
there is no reason to use atomic operations to read flags because they
cannot be update from somewhere eles.
2024-04-25 18:29:58 +02:00
Christopher Faulet
608e23c495 MINOR: peers: Use a static variable to wait a resync on reload
When a process is reloaded, the old process must performed a synchronisation
with the new process. To do so, the sync task notify the local peer to
proceed and waits. Internally, the sync task used PEERS_F_DONOTSTOP flag to
know it should wait. However, this flag was only set/unset in a single
function. There is no real reason to set a flag to do so. A static variable
set to 1 when the resync starts and to 0 when it is finished is enough.
2024-04-25 18:29:58 +02:00
Christopher Faulet
bdcfacdb78 MINOR: peers: Add comment on processing functions of the sync task
Just add a comment on __process_running_peer_sync() and
__process_stopping_peer_sync() functions.
2024-04-25 18:29:58 +02:00
Christopher Faulet
697bd69efc REORG: peers: Move peer and peers flags in the corresponding header file
PEER_F_* and PEERS_F_ * flags were moved to <peer-t.h> header file. It is
mandatory to decode them from "flags" dev tool.
2024-04-25 18:29:58 +02:00
Christopher Faulet
31f544209d MINOR: peers: Reorder and rename PEERS flags
Peers flags were renamed and reordered, mainly to move flags used for
debugging purpose at the end.

PEERS_F_RESYNC_LOCAL and PEERS_F_RESYNC_REMOTE were also renamed to
PEERS_F_RESYNC_LOCAL_FINISHED and PEERS_F_RESYNC_REMOTE_FINISHED to be clear
on the fact the operation is finished when the flag is set.
2024-04-25 18:29:58 +02:00
Christopher Faulet
17c4030aaa MINOR: peers: Reorder and slightly rename PEER flags
There are too many holes in peer flags. So let's reorder them. In addition,
PEER_F_RESYNC_REQUESTED flag was renamed to PEER_F_DBG_RESYNC_REQUESTED to
clearly state it is a flag set for debugging purpose.

Finally, PEER_TEACH_RESET was replaced by PEER_TEACH_FLAGS and the bitwise
complement operator is now used on lines updating the peer flags. It is a
far more common way to do (in HAProxy code at least) and less surprising.
2024-04-25 18:29:58 +02:00
Christopher Faulet
9934eebc19 MINOR: peers: Rename PEERS_F_TEACH_COMPLETE to PEERS_F_LOCAL_TEACH_COMPLETE
PEERS_F_TEACH_COMPLETE flag is only used for the old local peer to let the
sync task know it can stop waiting during a soft-stop. So it is less
confusing to rename this flag to clearly state it concerns local peer only.
2024-04-25 18:29:57 +02:00
Christopher Faulet
45f4698725 MINOR: peers: Start learning for local peer before receiving messages
A local peer assigned for leaning can immediately start to learn, without
sending any request. So we can do that first, before receiving
messages. This way, only PEER_LR_ST_PROCESSING state is evaluating when
received messages are processed.

In addition, when the resync request is sent, we are sure it is for a remote
peer.
2024-04-25 18:29:57 +02:00