Compare commits

..

868 Commits

Author SHA1 Message Date
Amaury Denoyelle
577fa44691 BUG/MINOR: quic: work around NEW_TOKEN parsing error on backend side
NEW_TOKEN frame is never emitted by a client, hence parsing was not
tested on frontend side.

On backend side, an issue can occur, as expected token length is static,
based on the token length used internally by haproxy. This is not
sufficient for most server implementation which uses larger token. This
causes a parsing error, which may cause skipping of following frames in
the same packet. This issue was detected using ngtcp2 as server.

As for now tokens are unused by haproxy, simply discard test on token
length during NEW_TOKEN frame parsing. The token itself is merely
skipped without being stored. This is sufficient for now to continue on
experimenting with QUIC backend implementation.

This does not need to be backported.
2025-06-12 17:47:15 +02:00
Amaury Denoyelle
830affc17d MINOR: server: reject QUIC servers without explicit SSL
Report an error during server configuration if QUIC is used by SSL is
not activiated via 'ssl' keyword. This is done in _srv_parse_finalize(),
which is both used by static and dynamic servers.

Note that contrary to listeners, an error is reported instead of a
warning, and SSL is not automatically activated if missing. This is
mainly due to the complex server configuration : _srv_parse_finalize()
is ideal to affect every servers, including dynamic entries. However, it
is executed after server SSL context allocation performed via
<prepare_srv> XPRT operation. A proper fix would be to move SSL ctx
alloc in _srv_parse_finalize(), but this may have unknown impact. Thus,
for now a simpler solution has been chosen.
2025-06-12 16:16:43 +02:00
Amaury Denoyelle
33cd96a5e9 BUG/MINOR: quic: prevent crash on startup with -dt
QUIC traces in ssl_quic_srv_new_ssl_ctx() are problematic as this
function is called early during startup. If activating traces via -dt
command-line argument, a crash occurs due to stderr sink not yet
available.

Thus, traces from ssl_quic_srv_new_ssl_ctx() are simply removed.

No backport needed.
2025-06-12 15:15:56 +02:00
Frederic Lecaille
5a0ae9e9be MINOR: quic-be: Avoid SSL context unreachable code without USE_QUIC_OPENSSL_COMPAT
This commit added a "err" C label reachable only with USE_QUIC_OPENSSL_COMPAT:

   MINOR: quic-be: Missing callbacks initializations (USE_QUIC_OPENSSL_COMPAT)

leading coverity to warn this:

*** CID 1611481:         Control flow issues  (UNREACHABLE)
/src/quic_ssl.c: 802             in ssl_quic_srv_new_ssl_ctx()
796     		goto err;
797     #endif
798
799      leave:
800     	TRACE_LEAVE(QUIC_EV_CONN_NEW);
801     	return ctx;
>>>     CID 1611481:         Control flow issues  (UNREACHABLE)
>>>     This code cannot be reached: "err:
SSL_CTX_free(ctx);".
802      err:
803     	SSL_CTX_free(ctx);
804     	ctx = NULL;
805     	TRACE_DEVEL("leaving on error", QUIC_EV_CONN_NEW);
806     	goto leave;
807     }

The less intrusive (without #ifdef) way to fix this it to add a "goto err"
statement from the code part which is reachable without USE_QUIC_OPENSSL_COMPAT.

Thank you to @chipitsine for having reported this issue in GH #3003.
2025-06-12 11:45:21 +02:00
Frederic Lecaille
869fb457ed BUG/MINOR: quic-be: CID double free upon qc_new_conn() failures
This issue may occur when qc_new_conn() fails after having allocated
and attached <conn_cid> to its tree. This is the case when compiling
haproxy against WolfSSL for an unknown reason at this time. In this
case the <conn_cid> is freed by pool_head_quic_connection_id(), then
freed again by quic_conn_release().

This bug arrived with this commit:

    MINOR: quic-be: QUIC connection allocation adaptation (qc_new_conn())

So, the aim of this patch is to free <conn_cid> only for QUIC backends
and if it is not attached to its tree. This is the case when <conn_id>
local variable passed with NULL value to qc_new_conn() is then intialized
to the same <conn_cid> value.
2025-06-12 11:45:21 +02:00
Frederic Lecaille
dc3fb3a731 CLEANUP: quic-be: Add comments about qc_new_conn() usage
This patch should have come with this last commit for the last qc_new_conn()
modifications for QUIC backends:

     MINOR: quic-be: get rid of ->li quic_conn member

qc_new_conn() must be passed NULL pointers for several variables as mentioned
by the comment. Some of these local variables are used to avoid too much
code modifications.
2025-06-12 11:45:21 +02:00
Amaury Denoyelle
603afd495b MINOR: hq-interop: encode request from HTX for backend side support
Implement transcoding of a HTX request into HTTP/0.9. This protocol is a
simplified version of HTTP. Request only supports GET method without any
header. As such, only a request line is written during snd_buf
operation.
2025-06-12 11:28:54 +02:00
Amaury Denoyelle
a286d5476b MINOR: hq-interop: decode response into HTX for backend side support
Implement transcoding of a HTTP/0.9 response into a HTX message.

HTTP/0.9 is a really simple substract of HTTP spec. The response does
not have any status line and is contains only the payload body. Response
is finished when the underlying connection/stream is closed.

A status line is generated to be compliant with HTX. This is performed
on the first invokation of rcv_buf for the current stream. Status code
is set to 200. Payload body if present is then copied using
htx_add_data().
2025-06-12 11:28:54 +02:00
Amaury Denoyelle
4031bf7432 MINOR: quic: wakeup backend MUX on handshake completed
This commit is the second and final step to initiate QUIC MUX on the
backend side. On handshake completion, MUX is woken up just after its
creation. This step is necessary to notify the stream layer, via the QCS
instance pre-initialized on MUX init, so that the transfer can be
resumed.

This mode of operation is similar to TCP stack when TLS+ALPN are used,
which forces MUX initialization to be delayed after handshake
completion.
2025-06-12 11:28:54 +02:00
Amaury Denoyelle
1efaca8a57 MINOR: mux-quic: instantiate first stream on backend side
Adjust qmux_init() to handle frontend and backend sides differently.
Most notably, on backend side, the first bidirectional stream is created
preemptively. This step is necessary as MUX layer will be woken up just
after handshake completion.
2025-06-12 11:28:54 +02:00
Amaury Denoyelle
f8d096c05f MINOR: mux-quic: set expect data only on frontend side
Stream data layer is notified that data is expected when FIN is
received, which marks the end of the HTTP request. This prepares data
layer to be able to handle the expected HTTP response.

Thus, this step is only relevant on frontend side. On backend side, FIN
marks the end of the HTTP response. No further content is expected, thus
expect data should not be set in this case.

Note that se_expect_data() invokation via qcs_attach_sc() is not
protected. This is because this function will only be called during
request headers parsing which is performed on the frontend side.
2025-06-12 11:28:54 +02:00
Amaury Denoyelle
e8775d51df MINOR: mux-quic: define flag for backend side
Mux connection is flagged with new QC_CF_IS_BACK if used on the backend
side. For now the only change is during traces, to be able to
differentiate frontend and backend usage.
2025-06-12 11:28:54 +02:00
Amaury Denoyelle
93b904702f MINOR: mux-quic: improve documentation for snd/rcv app-ops
Complete document for rcv_buf/snd_buf operations. In particular, return
value is now explicitely defined. For H3 layer, associated functions
documentation is also extended.
2025-06-12 11:28:54 +02:00
Amaury Denoyelle
e7f1db0348 MINOR: quic: mark ctrl layer as ready on quic_connect_server()
Use conn_ctrl_init() on the connection when quic_connect_server()
succeeds. This is necessary so that the connection is considered as
completely initialized. Without this, connect operation will be call
again if connection is reused.
2025-06-12 11:25:12 +02:00
Amaury Denoyelle
a0db93f3d8 MEDIUM: backend: delay MUX init with ALPN even if proto is forced
On backend side, multiplexer layer is initialized during
connect_server(). However, this step is not performed if ALPN is used,
as the negotiated protocol may be unknown. Multiplexer initialization is
delayed after TLS handshake completion.

There are still exceptions though that forces the MUX to be initialized
even if ALPN is used. One of them was if <mux_proto> server field was
already set at this stage, which is the case when an explicit proto is
selected on the server line configuration. Remove this condition so that
now MUX init is delayed with ALPN even if proto is forced.

The scope of this change should be minimal. In fact, the only impact
concerns server config with both proto and ALPN set, which is pretty
unlikely as it is contradictory.

The main objective of this patch is to prepare QUIC support on the
backend side. Indeed, QUIC proto will be forced on the server if a QUIC
address is used, similarly to bind configuration. However, we still want
to delay MUX initialization after QUIC handshake completion. This is
mandatory to know the selected application protocol, required during
QUIC MUX init.
2025-06-12 11:21:32 +02:00
Amaury Denoyelle
044ad3a602 BUG/MEDIUM: mux-quic: adjust wakeup behavior
Change wake callback behavior for QUIC MUX. This operation loops over
each QCS and notify their stream data layer on certain events via
internal helper qcc_wake_some_streams().

Previously, streams were notified only if an error occured on the
connection. Change this to notify streams data layer everytime wake
callback is used. This behavior is now identical to H2 MUX.

qcc_wake_some_streams() is also renamed to qcc_wake_streams(), as it
better reflect its true behavior.

This change should not have performance impact as wake mux ops should
not be called frequently. Note that qcc_wake_streams() can also be
called directly via qcc_io_process() to ensure a new error is correctly
propagated. As wake callback first uses qcc_io_process(), it will only
call qcc_wake_streams() if no error is present.

No known issue is associated with this commit. However, it could prevent
freezing transfer under certain condition. As such, it is considered as
a bug fix worthy of backporting.

This should be backported after a period of observation.
2025-06-12 11:12:49 +02:00
Christopher Faulet
2c3f3eaaed BUILD: hlua: Fix warnings about uninitialized variables (2)
It was still failing on Ubuntu-24.04 with GCC+ASAN. So, instead of
understand the code path the compiler followed to report uninitialized
variables, let's init them now.

No backport needed.
2025-06-12 10:49:54 +02:00
Aurelien DARRAGON
b5067a972c BUILD: listener: fix 'for' loop inline variable declaration
commit 16eb0fab3 ("MAJOR: counters: dispatch counters over thread groups")
introduced a build regression on some compilers:

  src/listener.c: In function 'listener_accept':
  src/listener.c:1095:3: error: 'for' loop initial declarations are only allowed in C99 mode
     for (int it = 0; it < global.nbtgroups; it++)
     ^
  src/listener.c:1095:3: note: use option -std=c99 or -std=gnu99 to compile your code
  src/listener.c:1101:4: error: 'for' loop initial declarations are only allowed in C99 mode
      for (int it = 0; it < global.nbtgroups; it++) {
      ^
  make: *** [src/listener.o] Error 1
  make: *** Waiting for unfinished jobs....

Let's fix that.
No backport needed
2025-06-12 08:46:36 +02:00
Christopher Faulet
01f011faeb BUILD: hlua: Fix warnings about uninitialized variables
In hlua_applet_tcp_recv_try() and hlua_applet_tcp_getline_yield(), GCC 14.2
reports warnings about 'blk2' variable that may be used uninitialized. It is
a bit strange because the code is pretty similar than before. But to make it
happy and to avoid bugs if the API change in future, 'blk2' is now used only
when its length is greater than 0.

No need to backport.
2025-06-12 08:46:36 +02:00
Christopher Faulet
8c573deb9f BUG/MINOR: hlua: Don't forget the return statement after a hlua_yieldk()
In hlua_applet_tcp_getline_yield(), the function may yield if there is no
data available. However we must take care to add a return statement just
after the call to hlua_yieldk(). I don't know the details of the LUA API,
but at least, this return statement fix a build error about uninitialized
variables that may be used.

It is a 3.3-specific issue. No backport needed.
2025-06-12 08:46:36 +02:00
Frederic Lecaille
bf6e576cfd MEDIUM: quic-be: initialize MUX on handshake completion
On backend side, MUX is instantiated after QUIC handshake completion.
This step is performed via qc_ssl_provide_quic_data(). First, connection
flags for handshake completion are resetted. Then, MUX is instantiated
via conn_create_mux() function.
2025-06-11 18:37:34 +02:00
Amaury Denoyelle
cdcecb9b65 MINOR: quic: define proper proto on QUIC servers
Force QUIC as <mux_proto> for server if a QUIC address is used. This is
similarly to what is already done for bind instances on the frontend
side. This step ensures that conn_create_mux() will select the proper
protocol.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
855fd63f90 MINOR: quic-be: Prevent the MUX to send/receive data
Such actions must be interrupted until the handshake completion.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
b9703cf711 MINOR: quic-be: get rid of ->li quic_conn member
Replace ->li quic_conn pointer to struct listener member by  ->target which is
an object type enum and adapt the code.
Use __objt_(listener|server)() where the object type is known. Typically
this is were the code which is specific to one connection type (frontend/backend).
Remove <server> parameter passed to qc_new_conn(). It is redundant with the
<target> parameter.
GSO is not supported at this time for QUIC backend. qc_prep_pkts() is modified
to prevent it from building more than an MTU. This has as consequence to prevent
qc_send_ppkts() to use GSO.
ssl_clienthello.c code is run only by listeners. This is why __objt_listener()
is used in place of ->li.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
f6ef3bbc8a MINOR: quic-be: SSL_get_peer_quic_transport_params() not defined by OpenSSL 3.5 QUIC API
Disable the code around SSL_get_peer_quic_transport_params() as this was done
for USE_QUIC_OPENSSL_COMPAT because SSL_get_peer_quic_transport_params() is not
defined by OpenSSL 3.5 QUIC API.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
034cf74437 MINOR: quic-be: Make the secret derivation works for QUIC backends (USE_QUIC_OPENSSL_COMPAT)
quic_tls_compat_keylog_callback() is the callback used by the QUIC OpenSSL
compatibility module to derive the TLS secrets from other secrets provided
by keylog. The <write> local variable to this function is initialized to denote
the direction (write to send, read to receive) the secret is supposed to be used
for. That said, as the QUIC cryptographic algorithms are symmetrical, the
direction is inversed between the peer: a secret which is used to write/send/cipher
data from a peer point of view is also the secret which is used to
read/receive/decipher data. This was confirmed by the fact that without this
patch, the TLS stack first provides the peer with Handshake to send/cipher
data. The client could not use such secret to decipher the Handshake packets
received from the server. This patch simply reverse the direction stored by
<write> variable to make the secrets derivation works for the QUIC client.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
d1cd0bb987 MINOR: quic-be: Missing callbacks initializations (USE_QUIC_OPENSSL_COMPAT)
quic_tls_compat_init() function is called from OpenSSL QUIC compatibility module
(USE_QUIC_OPENSSL_COMPAT) to initialize the keylog callback and the callback
which stores the QUIC transport parameters as a TLS extensions into the stack.
These callbacks must also be initialized for QUIC backends.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
fc90964b55 MINOR: quic-be: Store the remote transport parameters asap
This is done from TLS secrets derivation callback at Application level (the last
encryption level) calling SSL_get_peer_quic_transport_params() to have an access
to the TLS transport paremeters extension embedded into the Server Hello TLS message.
Then, quic_transport_params_store() is called to store a decoded version of
these transport parameters.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
8c2f2615f4 MINOR: quic-be: I/O handler switch adaptation
For connection to QUIC servers, this patch modifies the moment where the I/O
handler callback is switched to quic_conn_app_io_cb(). This is no more
done as for listener just after the handshake has completed but just after
it has been confirmed.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
f085a2f5bf MINOR: quic-be: Initial packet number space discarding.
Discard the Initial packet number space as soon as possible. This is done
during handshakes in quic_conn_io_cb() as soon as an Handshake packet could
be successfully sent.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
a62098bfb0 MINOR: quic-be: Add the conn object to the server SSL context
The initialization of <ssl_app_data_index> SSL user data index is required
to make all the SSL sessions to QUIC servers work as this is done for TCP
servers. The conn object notably retrieve for SSL callback which are
server specific (e.g. ssl_sess_new_srv_cb()).
2025-06-11 18:37:34 +02:00
Frederic Lecaille
e226a7cb79 MINOR: quic-be: Build post handshake frames
This action is not specific to listeners. A QUIC client also have to send
NEW_CONNECTION_ID frames.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
2d076178c6 MINOR: quic-be: Store asap the DCID
Store the peer connection ID (SCID) as the connection DCID as soon as an Initial
packet is received.
Stop comparing the packet to QUIC_PACKET_TYPE_0RTT is already match as
QUIC_PACKET_TYPE_INITIAL.
A QUIC server must not send too short datagram with ack-eliciting packets inside.
This cannot be done from quic_rx_pkt_parse() because one does not know if
there is ack-eliciting frame into the Initial packets. If the packet must be
dropped, this is after having parsed it!
2025-06-11 18:37:34 +02:00
Frederic Lecaille
b4a9b53515 MINOR: h3-be: Correctly retrieve h3 counters
This is done using qc_counters() function which supports also QUIC servers.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
e27b7b4889 MINOR: quic-be: Handshake packet number space discarding
This is done for QUIC clients (or haproxy QUIC servers) when the handshake is
confirmed.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
43d88a44f1 MINOR: quic-be: Datagrams and packet parsing support
Modify quic_dgram_parse() to stop passing it a listener as third parameter.
In place the object type address of the connection socket owner is passed
to support the haproxy servers with QUIC as transport protocol.
qc_owner_obj_type() is implemented to return this address.
qc_counters() is also implemented to return the QUIC specific counters of
the proxy of owner of the connection.
quic_rx_pkt_parse() called by quic_dgram_parse() is also modify to use
the object type address used by this latter as last parameter. It is
also modified to send Retry packet only from listeners. A QUIC client
(connection to haproxy QUIC servers) must drop the Initial packets with
non null token length. It is also not supposed to receive O-RTT packets
which are dropped.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
266b10b8a4 MINOR: quic-be: Do not redispatch the datagrams
The QUIC datagram redispatch is there to counter the race condition which
exists only for QUIC connections to listener where datagrams may arrive
on the wrong socket between the bind() and connect() calls.
Run this code part only for listeners.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
89d5a59933 MINOR: quic-be: add field for max_udp_payload_size into quic_conn
Add ->max_udp_payload_size new member to quic_conn struct.
Initialize it from qc_new_conn().
Adapt qc_snd_buf() to use it.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
f7c0f5ac1b MINOR: quic-be: xprt ->init() adapatations
Allocate a connection to connect to QUIC servers from qc_conn_init() which is the
->init() QUIC xprt callback.
Also initialize ->prepare_srv and ->destroy_srv callback as this done for TCP
servers.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
29fb1aee57 MINOR: quic-be: QUIC connection allocation adaptation (qc_new_conn())
For haproxy QUIC servers (or QUIC clients), the peer is considered as validated.
This is a property which is more specific to QUIC servers (haproxy QUIC listeners).
No <odcid> is used for the QUIC client connection. It is used only on the QUIC server side.
The <token_odcid> is also not used on the QUIC client side. It must be embedded into
the transport parameters only on the QUIC server side.
The quic_conn is created before the socket allocation. So, the local address is
zeroed.
Initilize the transport parameter with qc_srv_params_init().
Stop hardcoding the <server> parameter passed value to qc_new_isecs() to correctly
initialize the Initial secrets.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
9831f596ea MINOR: quic-be: ->connect() protocol callback adaptations
Modify quic_connect_server() which is the ->connect() callback for QUIC protocol:
    - add a BUG_ON() run when entering this funtion: the <fd> socket must equal -1
    - conn->handle is a union. conn->handle.qc is use for QUIC connection,
      conn->handle.fd must not be used to store the fd.
    - code alignment fix for setsockopt(fd, SOL_SOCKET, (SO_SNDBUF|SO_RCVBUF))
	  statements
    - remove the section of code which was duplicated from ->connect() TCP callback
    - fd_insert() the new socket file decriptor created to connect to the QUIC
      server with quic_conn_sock_fd_iocb() as callback for read event.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
52ec3430f2 MINOR: sock: Add protocol and socket types parameters to sock_create_server_socket()
This patch only adds <proto_type> new proto_type enum parameter and <sock_type>
socket type parameter to sock_create_server_socket() and adapts its callers.
This is to prepare the use of this function by QUIC servers/backends.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
9c84f64652 MINOR: quic-be: Add a function to initialize the QUIC client transport parameters
Implement qc_srv_params_init() to initialize the QUIC client transport parameters
in relation with connections to haproxy servers/backends.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
f49bbd36b9 MINOR: quic-be: SSL sessions initializations
Modify qc_alloc_ssl_sock_ctx() to pass the connection object as parameter. It is
NULL for a QUIC listener, not NULL for a QUIC server. This connection object is
set as value for ->conn quic_conn struct member. Initialise the SSL session object from
this function for QUIC servers.
qc_ssl_set_quic_transport_params() is also modified to pass the SSL object as parameter.
This is the unique parameter this function needs. <qc> parameter is used only for
the trace.
SSL_do_handshake() must be calle as soon as the SSL object is initialized for
the QUIC backend connection. This triggers the TLS CRYPTO data delivery.
tasklet_wakeup() is also called to send asap these CRYPTO data.
Modify the QUIC_EV_CONN_NEW event trace to dump the potential errors returned by
SSL_do_handshake().
2025-06-11 18:37:34 +02:00
Frederic Lecaille
1408d94bc4 MINOR: quic-be: ssl_sock contexts allocation and misc adaptations
Implement ssl_sock_new_ssl_ctx() to allocate a SSL server context as this is currently
done for TCP servers and also for QUIC servers depending on the <is_quic> boolean value
passed as new parameter. For QUIC servers, this function calls ssl_quic_srv_new_ssl_ctx()
which is specific to QUIC.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
7c76252d8a MINOR: quic-be: Correct the QUIC protocol lookup
From connect_server(), QUIC protocol could not be retreived by protocol_lookup()
because of the PROTO_TYPE_STREAM default passed as argument. In place to support
QUIC srv->addr_type.proto_type may be safely passed.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
1e45690656 MINOR: quic-be: Add a function for the TLS context allocations
Implement ssl_quic_srv_new_ssl_ctx() whose aim is to allocate a TLS context
for QUIC servers.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
a4e1296208 MINOR: quic-be: QUIC server xprt already set when preparing their CTXs
The QUIC servers xprts have already been set at server line parsing time.
This patch prevents the QUIC servers xprts to be reset to <ssl_sock> value which is
the value used for SSL/TCP connections.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
24fc44c44d MINOR: quic-be: QUIC backend XPRT and transport parameters init during parsing
Add ->quic_params new member to server struct.
Also set the ->xprt member of the server being initialized and initialize asap its
transport parameters from _srv_parse_init().
2025-06-11 18:37:34 +02:00
Frederic Lecaille
0e67687ca9 MINOR: quic-be: Call ->prepare_srv() callback at parsing time
This XPRT callback is called from check_config_validity() after the configuration
has been parsed to initialize all the SSL server contexts.

This patch implements the same thing for the QUIC servers.
2025-06-11 18:37:34 +02:00
Frederic Lecaille
5a711551a2 MINOR: quic-be: Version Information transport parameter check
Add a little check to verify that the version chosen by the server matches
with the client one. Initiliazes local transport parameters ->negotiated_version
value with this version if this is the case. If not, return 0;
2025-06-11 18:37:34 +02:00
Frederic Lecaille
990c9f95f7 MINOR: quic-be: Correct Version Information transp. param encoding
According to the RFC, a QUIC client must encode the QUIC version it supports
into the "Available Versions" of "Version Information" transport parameter
order by descending preference.

This is done defining <quic_version_2> and <quic_version_draft_29> new variables
pointers to the corresponding version of <quic_versions> array elements.
A client announces its available versions as follows: v1, v2, draft29.
2025-06-11 18:37:34 +02:00
Amaury Denoyelle
9c751a3cc1 MINOR: mux-quic-be: allow QUIC proto on backend side
Activate QUIC protocol support for MUX-QUIC on the backend side,
additionally to current frontend support. This change is mandatory to be
able to implement QUIC on the backend side.

Without this modification, it is impossible to activate explicitely QUIC
protocol on a server line, hence an error is reported :
  config : proxy 'xxxx' : MUX protocol 'quic' is not usable for server 'yyyy'
2025-06-11 18:37:34 +02:00
Amaury Denoyelle
f66b495f8e MINOR: server: mark QUIC support as experimental
Mark QUIC address support for servers as experimental on the backend
side. Previously, it was allowed but wouldn't function as expected. As
QUIC backend support requires several changes, it is better to declare
it as experimental first.
2025-06-11 18:37:33 +02:00
Amaury Denoyelle
bdd5e58179 MINOR: server: implement helper to identify QUIC servers
Define srv_is_quic() which can be used to quickly identified if a server
uses QUIC protocol.
2025-06-11 18:37:19 +02:00
Amaury Denoyelle
1ecf2e9bab BUG/MINOR: config/server: reject QUIC addresses
QUIC is not implemented on the backend side. To prevent any issue, it is
better to reject any server configured which uses it. This is done via
_srv_parse_init() which is used both for static and dynamic servers.

This should be backported up to all stable versions.
2025-06-11 18:37:17 +02:00
Christopher Faulet
b5525fe759 [RELEASE] Released version 3.3-dev1
Released version 3.3-dev1 with the following main changes :
    - BUILD: tools: properly define ha_dump_backtrace() to avoid a build warning
    - DOC: config: Fix a typo in 2.7 (Name format for maps and ACLs)
    - REGTESTS: Do not use REQUIRE_VERSION for HAProxy 2.5+ (5)
    - REGTESTS: Remove REQUIRE_VERSION=2.3 from all tests
    - REGTESTS: Remove REQUIRE_VERSION=2.4 from all tests
    - REGTESTS: Remove tests with REQUIRE_VERSION_BELOW=2.4
    - REGTESTS: Remove support for REQUIRE_VERSION and REQUIRE_VERSION_BELOW
    - MINOR: server: group postinit server tasks under _srv_postparse()
    - MINOR: stats: add stat_col flags
    - MINOR: stats: add ME_NEW_COMMON() helper
    - MINOR: proxy: collect per-capability stat in proxy_cond_disable()
    - MINOR: proxy: add a true list containing all proxies
    - MINOR: log: only run postcheck_log_backend() checks on backend
    - MEDIUM: proxy: use global proxy list for REGISTER_POST_PROXY_CHECK() hook
    - MEDIUM: server: automatically add server to proxy list in new_server()
    - MEDIUM: server: add and use srv_init() function
    - BUG/MAJOR: leastconn: Protect tree_elt with the lbprm lock
    - BUG/MEDIUM: check: Requeue healthchecks on I/O events to handle check timeout
    - CLEANUP: applet: Update comment for applet_put* functions
    - DEBUG: check: Add the healthcheck's expiration date in the trace messags
    - BUG/MINOR: mux-spop: Fix null-pointer deref on SPOP stream allocation failure
    - CLEANUP: sink: remove useless cleanup in sink_new_from_logger()
    - MAJOR: counters: add shared counters base infrastructure
    - MINOR: counters: add shared counters helpers to get and drop shared pointers
    - MINOR: counters: add common struct and flags to {fe,be}_counters_shared
    - MEDIUM: counters: manage shared counters using dedicated helpers
    - CLEANUP: counters: merge some common counters between {fe,be}_counters_shared
    - MINOR: counters: add local-only internal rates to compute some maxes
    - MAJOR: counters: dispatch counters over thread groups
    - BUG/MEDIUM: cli: Properly parse empty lines and avoid crashed
    - BUG/MINOR: config: emit warning for empty args only in discovery mode
    - BUG/MINOR: config: fix arg number reported on empty arg warning
    - BUG/MINOR: quic: Missing SSL session object freeing
    - MINOR: applet: Add API functions to manipulate input and output buffers
    - MINOR: applet: Add API functions to get data from the input buffer
    - CLEANUP: applet: Simplify a bit comments for applet_put* functions
    - MEDIUM: hlua: Update TCP applet functions to use the new applet API
    - BUG/MEDIUM: fd: Use the provided tgid in fd_insert() to get tgroup_info
    - BUG/MINIR: h1: Fix doc of 'accept-unsafe-...-request' about URI parsing
2025-06-11 14:31:33 +02:00
Christopher Faulet
b2f64af341 BUG/MINIR: h1: Fix doc of 'accept-unsafe-...-request' about URI parsing
The description of tests performed on the URI in H1 when
'accept-unsafe-violations-in-http-request' option is wrong. It states that
only characters below 32 and 127 are blocked when this option is set,
suggesting that otherwise, when it is not set, all invalid characters in the
URI, according to the RFC3986, are blocked.

But in fact, it is not true. By default all character below 32 and above 127
are blocked. And when 'accept-unsafe-violations-in-http-request' option is
set, characters above 127 (excluded) are accepted. But characters in
(33..126) are never checked, independently of this option.

This patch should fix the issue #2906. It should be backported as far as
3.0. For older versions, the docuementation could also be clarified because
this part is not really clear.

Note the request URI validation is still under discution because invalid
characters in (33.126) are never checked and some users request a stricter
parsing.
2025-06-10 19:17:56 +02:00
Olivier Houchard
6993981cd6 BUG/MEDIUM: fd: Use the provided tgid in fd_insert() to get tgroup_info
In fd_insert(), use the provided tgid to ghet the thread group info,
instead of using the one of the current thread, as we may call
fd_insert() from a thread of another thread group, that will happen at
least when binding the listeners. Otherwise we'd end up accessing the
thread mask containing enabled thread of the wrong thread group, which
can lead to crashes if we're binding on threads not present in the
thread group.
This should fix Github issue #2991.

This should be backported up to 2.8.
2025-06-10 15:10:56 +02:00
Christopher Faulet
9df380a152 MEDIUM: hlua: Update TCP applet functions to use the new applet API
The functions responsible to extract data from the applet input buffer or to
push data into the applet output buffer are now relying on the newly added
functions in the applet API. This simplifies a bit the code.
2025-06-10 08:16:10 +02:00
Christopher Faulet
18f9c71041 CLEANUP: applet: Simplify a bit comments for applet_put* functions
Instead of repeating which buffer is used depending on the API used by the
applet, a reference to applet_get_outbuf() was added.
2025-06-10 08:16:10 +02:00
Christopher Faulet
79445766a3 MINOR: applet: Add API functions to get data from the input buffer
There was already functions to pushed data from the applet to the stream by
inserting them in the right buffer, depending the applet was using or not
the legacy API. Here, functions to retreive data pushed to the applet by the
stream were added:

  * applet_getchar   : Gets one character

  * applet_getblk    : Copies a full block of data

  * applet_getword   : Copies one text block representing a word using a
                       custom separator as delimiter

  * applet_getline   : Copies one text line

  * applet_getblk_nc : Get one or two blocks of data

  * applet_getword_nc: Gets one or two blocks of text representing a word
                       using a custom separator as delimiter

  * applet_getline_nc: Gets one or two blocks of text representing a line
2025-06-10 08:16:10 +02:00
Christopher Faulet
0d8ecb1edc MINOR: applet: Add API functions to manipulate input and output buffers
In this patch, some functions were added to ease input and output buffers
manipulation, regardless the corresponding applet is using its own buffers
or it is relying on channels buffers. Following functions were added:

  * applet_get_inbuf  : Get the buffer containing data pushed to the applet
                        by the stream

  * applet_get_outbuf : Get the buffer containing data pushed by the applet
                        to the stream

  * applet_input_data : Return the amount of data in the input buffer

  * applet_skip_input : Skips <len> bytes from the input buffer

  * applet_reset_input: Skips all bytes from the input buffer

  * applet_output_room: Returns the amout of space available at the output
                        buffer

  * applet_need_room  : Indicates that the applet have more data to deliver
                        and it needs more room in the output buffer to do
			so
2025-06-10 08:16:10 +02:00
Frederic Lecaille
6b74633069 BUG/MINOR: quic: Missing SSL session object freeing
qc_alloc_ssl_sock_ctx() allocates an SSL_CTX object for each connection. It also
allocates an SSL object. When this function failed, it freed only the SSL_CTX object.
The correct way to free both of them is to call qc_free_ssl_sock_ctx().

Must be backported as far as 2.6.
2025-06-06 17:53:13 +02:00
Amaury Denoyelle
0cdf529720 BUG/MINOR: config: fix arg number reported on empty arg warning
If an empty argument is used in configuration, for example due to an
undefined environment variable, the rest of the line is not parsed. As
such, a warning is emitted to report this.

The warning was not totally correct as it reported the wrong argument
index. Fix this by this patch. Note that there is still an issue with
the "^" indicator, but this is not as easy to fix yet.

This is related to github issue #2995.

This should be backported up to 3.2.
2025-06-06 17:03:02 +02:00
Amaury Denoyelle
5f1fad1690 BUG/MINOR: config: emit warning for empty args only in discovery mode
Hide warning about empty argument outside of discovery mode. This is
necessary, else the message will be displayed twice, which hampers
haproxy output lisibility.

This should fix github isue #2995.

This should be backported up to 3.2.
2025-06-06 17:02:58 +02:00
Christopher Faulet
f5d41803d3 BUG/MEDIUM: cli: Properly parse empty lines and avoid crashed
Empty lines was not properly parsed and could lead to crashes because the
last argument was parsed outside of the cmdline buffer. Indeed, the last
argument is parsed to look for an eventual payload pattern. It is started
one character after the newline at the end of the command line. But it is
only valid for an non-empty command line.

So, now, this case is properly detected when we leave if an empty line is
detected.

This patch must be backported to 3.2.
2025-06-05 10:46:13 +02:00
Aurelien DARRAGON
16eb0fab31 MAJOR: counters: dispatch counters over thread groups
Most fe and be counters are good candidates for being shared between
processes. They are now grouped inside "shared" struct sub member under
be_counters and fe_counters.

Now they are properly identified, they would greatly benefit from being
shared over thread groups to reduce the cost of atomic operations when
updating them. For this, we take the current tgid into account so each
thread group only updates its own counters. For this to work, it is
mandatory that the "shared" member from {fe,be}_counters is initialized
AFTER global.nbtgroups is known, because each shared counter causes the stat
to be allocated lobal.nbtgroups times. When updating a counter without
concurrency, the first counter from the array may be updated.

To consult the shared counters (which requires aggregation of per-tgid
individual counters), some helper functions were added to counter.h to
ease code maintenance and avoid computing errors.
2025-06-05 09:59:38 +02:00
Aurelien DARRAGON
12c3ffbb48 MINOR: counters: add local-only internal rates to compute some maxes
cps_max (max new connections received per second), sps_max (max new
sessions per second) and http.rps_max (maximum new http requests per
second) all rely on shared counters (namely conn_per_sec, sess_per_sec and
http.req_per_sec). The problem is that shared counters are about to be
distributed over thread groups, and we cannot afford to compute the
total (for all thread groups) each time we update the max counters.

Instead, since such max counters (relying on shared counters) are a very
few exceptions, let's add internal (sess,conn,req) per sec freq counters
that are dedicated to cps_max, sps_max and http.rps_max computing.

Thanks to that, related *_max counters shouldn't be negatively impacted
by the thread-group distribution, yet they will not benefit from it
either. Related internal freq counters are prefixed with "_" to emphasize
the fact that they should not be used for other purpose (the shared ones,
which are about to be distributed over thread groups in upcoming commits
are still available and must be used instead). The internal ones could
eventually be removed at any time if we find another way to compute the
{cps,sps,http.rps)_max counters.
2025-06-05 09:59:31 +02:00
Aurelien DARRAGON
b72a8bb138 CLEANUP: counters: merge some common counters between {fe,be}_counters_shared
Now that we have a common struct between fe and be shared counters struct
let's perform some cleanup to merge duplicate members into the common
struct part. This will ease code maintenance.
2025-06-05 09:59:24 +02:00
Aurelien DARRAGON
b599138842 MEDIUM: counters: manage shared counters using dedicated helpers
proxies, listeners and server shared counters are now managed via helpers
added in one of the previous commits.

When guid is not set (ie: when not yet assigned), shared counters pointer
is allocated using calloc() (local memory) and a flag is set on the shared
counters struct to know how to manipulate (and free it). Else if guid is
set, then it means that the counters may be shared so while for now we
don't actually use a shared memory location the API is ready for that.

The way it works, for proxies and servers (for which guid is not known
during creation), we first call counters_{fe,be}_shared_get with guid not
set, which results in local pointer being retrieved (as if we just
manually called calloc() to retrieve a pointer). Later (during postparsing)
if guid is set we try to upgrade the pointer from local to shared.

Lastly, since the memory location for some objects (proxies and servers
counters) may change from creation to postparsing, let's update
counters->last_change member directly under counters_{fe,be}_shared_get()
so we don't miss it.

No change of behavior is expected, this is only preparation work.
2025-06-05 09:59:17 +02:00
Aurelien DARRAGON
c10ce1c85b MINOR: counters: add common struct and flags to {fe,be}_counters_shared
fe_counters_shared and be_counters_shared may share some common members
since they are quite similar, so we add a common struct part shared
between the two. struct counters_shared is added for convenience as
a generic pointer to manipulate common members from fe or be shared
counters pointer.

Also, the first common member is added: shared fe and be counters now
have a flags member.
2025-06-05 09:59:10 +02:00
Aurelien DARRAGON
aa53887398 MINOR: counters: add shared counters helpers to get and drop shared pointers
create include/haproxy/counters.h and src/counters.c files to anticipate
for further helpers as some counters specific tasks needs to be carried
out and since counters are shared between multiple object types (ie:
listener, proxy, server..) we need generic helpers.

Add some shared counters helper which are not yet used but will be updated
in upcoming commits.
2025-06-05 09:59:04 +02:00
Aurelien DARRAGON
a0dcab5c45 MAJOR: counters: add shared counters base infrastructure
Shareable counters are not tagged as shared counters and are dynamically
allocated in separate memory area as a prerequisite for being stored
in shared memory area. For now, GUID and threads groups are not taken into
account, this is only a first step.

also we ensure all counters are now manipulated using atomic operations,
namely, "last_change" counter is now read from and written to using atomic
ops.

Despite the numerous changes caused by the counters being moved away from
counters struct, no change of behavior should be expected.
2025-06-05 09:58:58 +02:00
Aurelien DARRAGON
89b04f2191 CLEANUP: sink: remove useless cleanup in sink_new_from_logger()
As reported by Ilya in GH #2994, some cleanup parts in
sink_new_from_logger() function are not used.

We can actually simplify the cleanup logic to remove dead code, let's
do that by renaming "error_final" label to "error" and only making use
of the "error" label, because sink_free() already takes care of proper
cleanup for all sink members.
2025-06-05 09:58:50 +02:00
Christopher Faulet
8c4bb8cab3 BUG/MINOR: mux-spop: Fix null-pointer deref on SPOP stream allocation failure
When we try to allocate a new SPOP stream, if an error is encountered,
spop_strm_destroy() is called to released the eventually allocated
stream. But, it must only be called if a stream was allocated. If the
reported error is an SPOP stream allocation failure, we must just leave to
avoid null-pointer dereference.

This patch should fix point 1 of the issue #2993. It must be backported as
far as 3.1.
2025-06-04 08:48:49 +02:00
Christopher Faulet
6786b05297 DEBUG: check: Add the healthcheck's expiration date in the trace messags
It could help to diagnose some issues about timeout processing. So let's add
it !
2025-06-03 15:06:12 +02:00
Christopher Faulet
8ee650a88b CLEANUP: applet: Update comment for applet_put* functions
These functions were copied from the channel API and modified to work with
applets using the new API or the legacy one. However, the comments were
updated accordingly. It is the purpose of this patch.
2025-06-03 15:03:30 +02:00
Christopher Faulet
7c788f0984 BUG/MEDIUM: check: Requeue healthchecks on I/O events to handle check timeout
When a healthchecks is processed, once the first wakeup passed to start the
check, and as long as the expiration timer is not reached, only I/O events
are able to wake it up. It is an issue when there is a check timeout
defined.  Especially if the connect timeout is high and the check timeout is
low. In that case, the healthcheck's task is never requeue to handle any
timeout update. When the connection is established, the check timeout is set
to replace the connect timeout. It is thus possible to report a success
while a timeout should be reported.

So, now, when an I/O event is handled, the healthcheck is requeue, except if
an success or an abort is reported.

Thanks to Thierry Fournier for report and the reproducer.

This patch must be backported to all stable versions.
2025-06-03 15:03:30 +02:00
Olivier Houchard
913b2d6c83 BUG/MAJOR: leastconn: Protect tree_elt with the lbprm lock
In fwlc_srv_reposition(), set the server's tree_elt while we still hold
the lbprm read lock. While it was protected from concurrent
fwlc_srv_reposition() calls by the server's lb_lock, it was not from
dequeuing/requeuing that could occur if the server gets down/up or its
weight is changed, and that would lead to inconsistencies, and the
watchdog killing the process because it is stuck in an infinite loop in
fwlc_get_next_server().

This hopefully fixes github issue #2990.

This should be backported to 3.2.
2025-06-03 04:42:47 +02:00
Aurelien DARRAGON
368d01361a MEDIUM: server: add and use srv_init() function
rename _srv_postparse() internal function to srv_init() function and group
srv_init_per_thr() plus idle conns list init inside it. This way we can
perform some simplifications as srv_init() performs multiple server
init steps after parsing.

SRV_F_CHECKED flag was added, it is automatically set when srv_init()
runs successfully. If the flag is already set and srv_init() is called
again, nothing is done. This permis to manually call srv_init() earlier
than the default POST_CHECK hook when needed without risking to do things
twice.
2025-06-02 17:51:33 +02:00
Aurelien DARRAGON
889ef6f67b MEDIUM: server: automatically add server to proxy list in new_server()
while new_server() takes the parent proxy as argument and even assigns
srv->proxy to the parent proxy, it didn't actually inserted the server
to the parent proxy server list on success.

The result is that sometimes we add the server to the list after
new_server() is called, and sometimes we don't.

This is really error-prone and because of that hooks such as
REGISTER_POST_SERVER_CHECK() which as run for all servers listed in
all proxies may not be relied upon for servers which are not actually
inserted in their parent proxy server list. Plus it feels very strange
to have a server that points to a proxy, but then the proxy doesn't know
about it because it cannot find it in its server list.

To prevent errors and make proxy->srv list reliable, we move the insertion
logic directly under new_server(). This requires to know if we are called
during parsing or during runtime to either insert or append the server to
the parent proxy list. For that we use PR_FL_CHECKED flag from the parent
proxy (if the flag is set, then the proxy was checked so we are past the
init phase, thus we assume we are called during runtime)

This implies that during startup if new_server() has to be cancelled on
error paths we need to call srv_detach() (which is now exposed in server.h)
before srv_drop().

The consequence of this commit is that REGISTER_POST_SERVER_CHECK() should
not run reliably on all servers created using new_server() (without having
to manually loop on global servers_list)
2025-06-02 17:51:30 +02:00
Aurelien DARRAGON
e262e4bbe4 MEDIUM: proxy: use global proxy list for REGISTER_POST_PROXY_CHECK() hook
REGISTER_POST_PROXY_CHECK() used to iterate over "main" proxies to run
registered callbacks. This means hidden proxies (and their servers) did
not get a chance to get post-checked and could cause issues if some post-
checks are expected to be executed on all proxies no matter their type.

Instead we now rely on the global proxies list. Another side effect is that
the REGISTER_POST_SERVER_CHECK() now runs as well for servers from proxies
that are not part of the main proxies list.
2025-06-02 17:51:27 +02:00
Aurelien DARRAGON
1f12e45b0a MINOR: log: only run postcheck_log_backend() checks on backend
postcheck_log_backend() checks are executed no matter if the proxy
actually has the backend capability while the checks actually depend
on this.

Let's fix that by adding an extra condition to ensure that the BE
capability is set.

This issue is not tagged as a bug because for now it remains impossible
to have a syslog proxy without BE capability in the main proxy list, but
this may change in the future.
2025-06-02 17:51:24 +02:00
Aurelien DARRAGON
943958c3ff MINOR: proxy: add a true list containing all proxies
We have global proxies_list pointer which is announced as the list of
"all existing proxies", but in fact it only represents regular proxies
declared on the config file through "listen, frontend or backend" keywords

It is ambiguous, and we currently don't have a straightforwrd method to
iterate over all proxies (either public or internal ones) within haproxy

Instead we still have to manually iterate over multiple lists (main
proxies, log-forward proxies, peer proxies..) which is error-prone.

In this patch we add a struct list member (8 bytes) inside struct proxy
in order to store every proxy (except default ones) within a global
"proxies" list which is actually representative for all proxies existing
under haproxy process, like we already have for servers.
2025-06-02 17:51:21 +02:00
Aurelien DARRAGON
6ccf770fe2 MINOR: proxy: collect per-capability stat in proxy_cond_disable()
proxy_cond_disable() collects and prints cumulated connections for be and
fe proxies no matter their type. With shared stats it may cause issues
because depending on the proxy capabilities only fe or be counters may
be allocated.

In this patch we add some checks to ensure we only try to read from
valid memory locations, else we rely on default values (0).
2025-06-02 17:51:17 +02:00
Aurelien DARRAGON
c7c017ec3c MINOR: stats: add ME_NEW_COMMON() helper
Split ME_NEW_* helper into COMMON part and specific part so it becomes
easier to add alternative helpers without code duplication.
2025-06-02 17:51:12 +02:00
Aurelien DARRAGON
d04843167c MINOR: stats: add stat_col flags
Add stat_col flags member to store .generic bit and prepare for upcoming
flags. No functional change expected.
2025-06-02 17:51:08 +02:00
Aurelien DARRAGON
f0b40b49b8 MINOR: server: group postinit server tasks under _srv_postparse()
init_srv_requeue() and init_srv_slowstart() functions are called after
initial server parsing via REGISTER_POST_SERVER_CHECK() hook, and they
are also manually called for dynamic server after the server is
initialized.

This may conflict with _srv_postparse() which is also registered via
REGISTER_POST_SERVER_CHECK() and called during dynamic server creation

To ensure functions don't conflict with each other, let's ensure they
are executed in proper order by calling init_srv_requeue and
init_srv_slowstart() from _srv_postparse() which now becomes the parent
function for server related postparsing stuff. No change of behavior is
expected.
2025-06-02 17:51:05 +02:00
Tim Duesterhus
8ee8b8a04d REGTESTS: Remove support for REQUIRE_VERSION and REQUIRE_VERSION_BELOW
This is no longer used since the migration to the native `haproxy -cc
'version_atleast(X)'` functionality.

see 8727614dc4046e91997ecce421bcb6a5537cac93
see 5efc48dcf1b133dd415c759e83b21d52dc303786
2025-06-02 17:37:11 +02:00
Tim Duesterhus
d8951ec70f REGTESTS: Remove tests with REQUIRE_VERSION_BELOW=2.4
HAProxy 2.4 is the lowest supported version, thus this never matches.

see 18cd4746e5aff9da78d16220b0412947ceba24f3
2025-06-02 17:37:07 +02:00
Tim Duesterhus
534b09f2a2 REGTESTS: Remove REQUIRE_VERSION=2.4 from all tests
HAProxy 2.4 is the lowest supported version, thus this always matches.

see 7aff1bf6b90caadfa95f6b43b526275191991d6f
2025-06-02 17:37:04 +02:00
Tim Duesterhus
239785fd27 REGTESTS: Remove REQUIRE_VERSION=2.3 from all tests
HAProxy 2.4 is the lowest supported version, thus this always matches.

see 7aff1bf6b90caadfa95f6b43b526275191991d6f
2025-06-02 17:37:00 +02:00
Tim Duesterhus
294c47a5ef REGTESTS: Do not use REQUIRE_VERSION for HAProxy 2.5+ (5)
Introduced in:

25bcdb1d9 BUG/MAJOR: h1: Be stricter on request target validation during message parsing

see also:

fbbbc33df REGTESTS: Do not use REQUIRE_VERSION for HAProxy 2.5+
2025-06-02 17:36:56 +02:00
Christopher Faulet
8e8cdf114b DOC: config: Fix a typo in 2.7 (Name format for maps and ACLs)
"identified" was used instead of "identifier". May be backported as far as
3.0
2025-06-02 09:19:38 +02:00
Willy Tarreau
b88164d9c0 BUILD: tools: properly define ha_dump_backtrace() to avoid a build warning
In resolve_sym_name() we declare a few symbols that we want to be able
to resolve. ha_dump_backtrace() was declared with a struct buffer instead
of a pointer to such a struct, which has no effect since we only want to
get the function's pointer, but produces a build warning with LTO, so
let's fix it.

This can be backported to 3.0.
2025-05-30 17:15:48 +02:00
Willy Tarreau
9f4cd435d3 [RELEASE] Released version 3.3-dev0
Released version 3.3-dev0 with the following main changes :
    - MINOR: version: mention that it's development again
2025-05-28 16:46:34 +02:00
Willy Tarreau
8809251ee0 MINOR: version: mention that it's development again
This essentially reverts a6458fd4269.
2025-05-28 16:46:15 +02:00
Willy Tarreau
e134140d28 [RELEASE] Released version 3.2.0
Released version 3.2.0 with the following main changes :
    - MINOR: promex: Add agent check status/code/duration metrics
    - MINOR: ssl: support strict-sni in ssl-default-bind-options
    - MINOR: ssl: also provide the "tls-tickets" bind option
    - MINOR: server: define CLI I/O handler for "add server"
    - MINOR: server: implement "add server help"
    - MINOR: server: use stress mode for "add server help"
    - BUG/MEDIUM: server: fix crash after duplicate GUID insertion
    - BUG/MEDIUM: server: fix potential null-deref after previous fix
    - MINOR: config: list recently added sections with -dKcfg
    - BUG/MAJOR: cache: Crash because of wrong cache entry deleted
    - DOC: configuration: fix the example in crt-store
    - DOC: config: clarify the wording around single/double quotes
    - DOC: config: clarify the legacy cookie and header captures
    - DOC: config: fix alphabetical ordering of layer 7 sample fetch functions
    - DOC: config: fix alphabetical ordering of layer 6 sample fetch functions
    - DOC: config: fix alphabetical ordering of layer 5 sample fetch functions
    - DOC: config: fix alphabetical ordering of layer 4 sample fetch functions
    - DOC: config: fix alphabetical ordering of internal sample fetch functions
    - BUG/MINOR: h3: Set HTX flags corresponding to the scheme found in the request
    - BUG/MEDIUM: h3: Declare absolute URI as normalized when a :authority is found
    - DOC: config: mention in bytes_in and bytes_out that they're read on input
    - DOC: config: clarify the basics of ACLs (call point, multi-valued etc)
    - REGTESTS: Make the script testing conditional set-var compatible with Vtest2
    - REGTESTS: Explicitly allow failing shell commands in some scripts
    - MINOR: listeners: Add support for a label on bind line
    - BUG/MEDIUM: cli/ring: Properly handle shutdown in "show event" I/O handler
    - BUG/MEDIUM: hlua: Properly detect shudowns for TCP applets based on the new API
    - BUG/MEDIUM: hlua: Fix getline() for TCP applets to work with applet's buffers
    - BUG/MEDIUM: hlua: Fix receive API for TCP applets to properly handle shutdowns
    - CI: vtest: Rely on VTest2 to run regression tests
    - CI: vtest: Fix the build script to properly work on MaOS
    - CI: combine AWS-LC and AWS-LC-FIPS by template
    - BUG/MEDIUM: httpclient: Throw an error if an lua httpclient instance is reused
    - DOC: hlua: Add a note to warn user about httpclient object reuse
    - DOC: hlua: fix a few typos in HTTPMessage.set_body_len() documentation
    - DEV: patchbot: prepare for new version 3.3-dev
    - MINOR: version: mention that it's 3.2 LTS now.
2025-05-28 16:35:14 +02:00
Willy Tarreau
a6458fd426 MINOR: version: mention that it's 3.2 LTS now.
The version will be maintained up to around Q2 2030. Let's
also update the INSTALL file to mention this.
2025-05-28 16:31:27 +02:00
Willy Tarreau
2502435eb3 DEV: patchbot: prepare for new version 3.3-dev
The bot will now load the prompt for the upcoming 3.2 version so we have
to rename the files and update their contents to match the current version.
2025-05-28 16:23:12 +02:00
Willy Tarreau
21ce685fcd DOC: hlua: fix a few typos in HTTPMessage.set_body_len() documentation
A few typos were noticed while gathering info for the 3.2 announce
messages, this fixes them, and will probably constitute the last
commit of this release. There's no need to backport it unless commit
94055a5e7 ("MEDIUM: hlua: Add function to change the body length of
an HTTP Message") is backported.
2025-05-27 19:33:49 +02:00
Christopher Faulet
cb7a2444d1 DOC: hlua: Add a note to warn user about httpclient object reuse
It is not supported to reuse an lua httpclient instance to process several
requests. A new object must be created for each request. Thanks to the
previous patch ("BUG/MEDIUM: httpclient: Throw an error if an lua httpclient
instance is reused"), an error is now reported if this happens. But it is
not obvious for users. So the lua-api docuementation was updated accordingly.

This patch is related to issue #2986. It should be backported with the
commit above.
2025-05-27 18:48:23 +02:00
Christopher Faulet
50fca6f0b7 BUG/MEDIUM: httpclient: Throw an error if an lua httpclient instance is reused
It is not expected/supported to reuse an httpclient instance to process
several requests. A new instance must be created for each request. However,
in lua, there is nothing to prevent a user to create an httpclient object
and use it in a loop to process requests.

That's unfortunate because this will apparently work, the requests will be
sent and a response will be received and processed. However internally some
ressources will be allocated and never released. When the next response is
processed, the ressources allocated for the previous one are definitively
lost.

In this patch we take care to check that the httpclient object was never
used when a request is sent from a lua script by checking
HTTPCLIENT_FS_STARTED flags. This flag is set when a httpclient applet is
spawned to process a request and never removed after that. In lua, the
httpclient applet is created when the request is sent. So, it is the right
place to do this test.

This patch should fix the issue #2986. It should be backported as far as
2.6.
2025-05-27 18:47:24 +02:00
Ilya Shipitsin
94ded5523f CI: combine AWS-LC and AWS-LC-FIPS by template
let's reduce code duplication by involving workflow templates
2025-05-27 15:06:58 +02:00
Christopher Faulet
508e074a32 CI: vtest: Fix the build script to properly work on MaOS
"config.h" header file is new in VTest2 and includes must be adapted to be
able to build VTest on MacOS. Let's add "-I." to make it work.
2025-05-27 14:48:53 +02:00
Christopher Faulet
6a18d28ba2 CI: vtest: Rely on VTest2 to run regression tests
VTest2 (https://github.com/vtest/VTest2) was released and is a remplacement
for VTest. VTest was archived. So let's use the new version now.

If this commit is backported, the 2 following commits must also be
backported:

 * 2808e3577 ("REGTESTS: Explicitly allow failing shell commands in some scripts")
 * 82c291124 ("REGTESTS: Make the script testing conditional set-var compatible with Vtest2")
2025-05-27 14:38:46 +02:00
Christopher Faulet
bc4c3c7969 BUG/MEDIUM: hlua: Fix receive API for TCP applets to properly handle shutdowns
An optional timeout was added to AppletTCP.receive() to interrupt calls after a
delay. It was mandatory to be able to implement interactive applets (like
trisdemo). However, this broke the API and it made impossible to differentiate
the shutdowns from the delays expirations. Indeed, in both cases, an empty
string was returned.

Because historically an empty string was used to notify a connection shutdown,
it should not be changed. So now, 'nil' value is returned when no data was
available before the delay expiration.

The new AppletTCP:try_receive() function was also affected. To fix it, instead
of stating there is no delay when a receive is tried, an expired delay is
set. Concretely TICK_ETERNITY was replaced by now_ms.

Finally, AppletTCP:getline() function is not concerned for now because there
is no way to interrupt it after some delay.

The documentation and trisdemo lua script were updated accordingly.

This patch depends on "BUG/MEDIUM: hlua: Properly detect shudowns for TCP
applets based on the new API". However, it is a 3.2-specific issue, so no
backport is needed.
2025-05-27 07:53:19 +02:00
Christopher Faulet
c0ecef71d7 BUG/MEDIUM: hlua: Fix getline() for TCP applets to work with applet's buffers
The commit e5e36ce09 ("BUG/MEDIUM: hlua/cli: Fix lua CLI commands to work
with applet's buffers") fixed the TCP applets API to work with applets using
its own buffers. Howver the getline() function was not updated. It could be
an issue for anyone registering a CLI commands reading lines.

This patch should be backported as far as 3.0.
2025-05-27 07:53:01 +02:00
Christopher Faulet
c64781c2c8 BUG/MEDIUM: hlua: Properly detect shudowns for TCP applets based on the new API
The internal function responsible to receive data for TCP applets with
internal buffers is buggy. Indeed, for these applets, the buffer API is used
to get data. So there is no tests on the SE to properly detect connection
shutdowns. So, it must be performed by hand after the call to b_getblk_nc().

This patch must be backported as far as 3.0.
2025-05-26 19:00:00 +02:00
Christopher Faulet
4d4da515f2 BUG/MEDIUM: cli/ring: Properly handle shutdown in "show event" I/O handler
The commit 03dc54d802 ("BUG/MINOR: ring: Fix I/O handler of "show event"
command to not rely on the SC") introduced a regression. By removing
dependencies on the SC, a test to detect client shutdowns was removed. So
now, the CLI applet is no longer released when the client shut the
connection during a "show event -w".

So of course, we should not use the SC to detect the shutdowns. But the SE
must be used insteead.

It is a 3.2-specific issue, so no backport needed.
2025-05-26 19:00:00 +02:00
Christopher Faulet
99e755d673 MINOR: listeners: Add support for a label on bind line
It is now possile to set a label on a bind line. All sockets attached to
this bind line inherits from this label. The idea is to be able to groud of
sockets. For now, there is no mechanism to create these groups, this must be
done by hand.
2025-05-26 19:00:00 +02:00
Christopher Faulet
2808e3577f REGTESTS: Explicitly allow failing shell commands in some scripts
Vtest2, that should replaced Vtest in few months, will reject any failing
commands in shell blocks. However, some scripts are executing some commands,
expecting an error to be able to parse the error output. So, now use "set
+e" in those scripts to explicitly state failing commads are expected.

It is just used for non-final commands. At the end, the shell block must
still report a success.
2025-05-26 19:00:00 +02:00
Christopher Faulet
82c2911248 REGTESTS: Make the script testing conditional set-var compatible with Vtest2
VTest2 will replaced VTest in few months. There is not so much change
expected. One of them is that a User-Agent header is added by default in all
requests, except if an custom one is already set or if "-nouseragent" option
is used. To still be compatible with VTest, it is not possible to use the
option to avoid the header addition. So, a custom user-agent is added in the
last test of "sample_fetches/cond_set_var.vtc" to be sure it will pass with
Vtest and Vtest2. It is mandatory because the request length is tested.
2025-05-26 19:00:00 +02:00
Willy Tarreau
5b937b7a97 DOC: config: clarify the basics of ACLs (call point, multi-valued etc)
This is essentially in order to address the concerns expressed in
issue #2226 where it is mentioned that the moment they are called is
not clear enough. Admittedly, re-reading the paragraph doesn't make
it obvious on a quick read that they behave like functions. This patch
adds an extra paragraph that makes the parallel with programming
languages' boolean functions and explains the fact that they can be
multi-valued. Hoping this is clearer now.
2025-05-26 16:25:22 +02:00
Willy Tarreau
ef9511be90 DOC: config: mention in bytes_in and bytes_out that they're read on input
Issue #2267 suggests that it's unclear what exactly the byte counts mean
(particularly when compression is involved). Let's clarify that the counts
are read on data input and that they also cover headers and a bit of
internal overhead.
2025-05-26 15:54:36 +02:00
Christopher Faulet
e70c23e517 BUG/MEDIUM: h3: Declare absolute URI as normalized when a :authority is found
Since commit 2c3d656f8 ("MEDIUM: h3: use absolute URI form with
:authority"), the absolute URI form is used when a ':authority'
pseudo-header is found. However, this URI was not declared as normalized
internally.  So, when the request is reformated to be sent to an h1 server,
the absolute-form is used instead of the origin-form. It is unexpected and
may be an issue for some servers that could reject the request.

So, now, we take care to set HTX_SL_F_HAS_AUTHORITY flag on the HTX message
when an authority was found and HTX_SL_F_NORMALIZED_URI flag is set for
"http" or "https" schemes.

No backport needed because the commit above must not be backported. It
should fix a regression reported on the 3.2-dev17 in issue #2977.

This commit depends on "BUG/MINOR: h3: Set HTX flags corresponding to the
scheme found in the request".
2025-05-26 11:47:23 +02:00
Christopher Faulet
da9792cca8 BUG/MINOR: h3: Set HTX flags corresponding to the scheme found in the request
When a ":scheme" pseudo-header is found in a h3 request, the
HTX_SL_F_HAS_SCHM flag must be set on the HTX message. And if the scheme is
'http' or 'https', the corresponding HTX flag must also be set. So,
respectively, HTX_SL_F_SCHM_HTTP or HTX_SL_F_SCHM_HTTPS.

It is mainly used to send the right ":scheme" pseudo-header value to H2
server on backend side.

This patch could be backported as far as 2.6.
2025-05-26 11:38:29 +02:00
Willy Tarreau
083708daf8 DOC: config: fix alphabetical ordering of internal sample fetch functions
Some misordering has been accumulating over time, making some of them
hard to spot. Also "uptime" was not indexed.
2025-05-26 09:36:23 +02:00
Willy Tarreau
52c2247d90 DOC: config: fix alphabetical ordering of layer 4 sample fetch functions
Some misordering has been accumulating over time, making some of them
hard to spot.
2025-05-26 09:33:17 +02:00
Willy Tarreau
770098f5e3 DOC: config: fix alphabetical ordering of layer 5 sample fetch functions
Some misordering has been accumulating over time, making some of them
hard to spot.
2025-05-26 09:26:11 +02:00
Willy Tarreau
5261e35b8f DOC: config: fix alphabetical ordering of layer 6 sample fetch functions
Some misordering has been accumulating over time, making some of them
hard to spot.
2025-05-26 09:26:11 +02:00
Willy Tarreau
e9248243e9 DOC: config: fix alphabetical ordering of layer 7 sample fetch functions
Some misordering has been accumulating over time, making some of them
hard to spot.
2025-05-26 09:26:11 +02:00
Willy Tarreau
38456f63a3 DOC: config: clarify the legacy cookie and header captures
As reported in issue #2195, cookie captures and header captures are no
longer the recommended way to proceed. Let's mention that this is the
legacy way and provide a few pointers to the recommended functions and
actions to use the modern methods.
2025-05-26 08:56:33 +02:00
Willy Tarreau
da8d6d1b2c DOC: config: clarify the wording around single/double quotes
As reported in issue #2327, the wording used in the section about quoting
can be read two ways due to the use of the two types of quotes to protect
each other quote. Better only use the quoting without mixing the two when
mentioning them.
2025-05-26 08:36:33 +02:00
William Lallemand
d607940915 DOC: configuration: fix the example in crt-store
Fix a bad example in the crt-store section. site1 does not use the "web"
crt-store but the global one.

Must be backported as far as 3.0 however the section was 3.12 in
previous version.
2025-05-25 16:55:08 +02:00
Remi Tricot-Le Breton
90441e9bfe BUG/MAJOR: cache: Crash because of wrong cache entry deleted
When "vary" is enabled, we can have multiple entries for a given primary
key in the cache tree. There is a limit to how many secondary entries
can be inserted for a given key. When we try to insert a new secondary
entry, if the limit is already reached, we can try to find expired
entries with the same primary key, and if the limit is still reached we
want to abort the current insertion and to remove the node that was just
inserted.

In commit "a29b073: MEDIUM: cache: Add refcount on cache_entry" though,
a regression was introduced. Instead of removing the entry just inserted
as the comments suggested, we removed the second to last entry and
returned NULL. We then reset the eb.key of the cache_entry in the caller
because we assumed that the entry was already removed from the tree.

This means that some entries with an empty key were wrongly kept in the
tree and the last secondary entry, which keeps the number of secondary
entries of a given key was removed.

This ended up causing some crashes later on when we tried to iterate
over the elements of this given key. The crash could occur in multiple
places, either when trying to retrieve an entry or to add some new ones.

This crash was raised in GitHub issue #2950.
The fix should be backported up to 3.0.
2025-05-23 22:38:54 +02:00
Willy Tarreau
84ffb3d0a9 MINOR: config: list recently added sections with -dKcfg
Newly added sections (crt-store, traces, acme) were not listed in
-dKcfg, let's add them. For now they have to be manually enumerated.
2025-05-23 10:49:33 +02:00
Willy Tarreau
28c7a22790 BUG/MEDIUM: server: fix potential null-deref after previous fix
A valid build warning was reported in the CI with latest commit b40ce97ecc
("BUG/MEDIUM: server: fix crash after duplicate GUID insertion"). Indeed,
if the first test in the function fails, we branch to the err label
with guid==NULL and will crash there. Let's just test guid before
dereferencing it for freeing.

This needs to be backported to 3.0 as well since the commit above was
meant to go there.
2025-05-22 18:09:12 +02:00
Amaury Denoyelle
b40ce97ecc BUG/MEDIUM: server: fix crash after duplicate GUID insertion
On "add server", if a GUID is defined, guid_insert() is used to add the
entry into the global GUID tree. If a similar entry already exists, GUID
insertion fails and the server creation is eventually aborted.

A crash could occur in this case because of an invalid memory access via
guid_remove(). The latter is caused via free_server() as the server
insertion is rejected. The invalid occurs on GUID key.

The issue occurs because of guid_insert(). The function properly
deallocates the GUID key on duplicate insertion, but it failed to reset
<guid.node.key> to NULL. This caused the invalid memory access on
guid_remove(). To fix this, ensure that key member is properly resetted
on guid_insert() error path.

This must be backported up to 3.0.
2025-05-22 17:59:37 +02:00
Amaury Denoyelle
5e088e3f8e MINOR: server: use stress mode for "add server help"
Implement stress mode on "add server help". This ensures that the
command is fully reentrant on full output buffer.

For testing, it requires compilation with USE_STRESS and global setting
"stress-level 1".
2025-05-22 17:40:05 +02:00
Amaury Denoyelle
4de5090976 MINOR: server: implement "add server help"
Implement "help" as a sub-command for "add server" CLI. The objective is
to list all the keywords that are supported for dynamic servers. CLI IO
handler and add_srv_ctx are used to support reentrancy on full output
buffer.

Now that this command is implemented, the outdated keyword list on "add
server" from management documentation can be removed.
2025-05-22 17:40:05 +02:00
Amaury Denoyelle
2570892c41 MINOR: server: define CLI I/O handler for "add server"
Extend "add server" to support an IO handler function named
cli_io_handler_add_server(). A context object is also defined whose
usage will depend on IO handler capabilities.

IO handler is skipped when "add server" is run in default mode, i.e. on
a dynamic server creation. Thus, currently IO handler is unneeded.
However, it will become useful to support sub-commands for "add server".

Note that return value of "add server" parser has been changed on server
creation success. Previously, it was used incorrectly to report if
server was inserted or not. In fact, parser return value is used by CLI
generic code to detect if command processing has been completed, or
should continue to the IO handler. Now, "add server" always returns 1 to
signal that CLI processing is completed. This is necessary to preserve
CLI output emitted by parser, even now that IO handler is defined for
the command. Previously, output was emitted in every situations due to
IO handler not defined. See below code snippet from cli.c for a better
overview :

  if (kw->parse && kw->parse(args, payload, appctx, kw->private) != 0) {
          ret = 1;
          goto fail;
  }

  /* kw->parse could set its own io_handler or io_release handler */
  if (!appctx->cli_ctx.io_handler) {
          ret = 1;
          goto fail;
  }

  appctx->st0 = CLI_ST_CALLBACK;
  ret = 1;
  goto end;
2025-05-22 17:40:05 +02:00
Willy Tarreau
1c0f2e62ad MINOR: ssl: also provide the "tls-tickets" bind option
Currently there is "no-tls-tickets" that is also supported in the
ssl-default-bind-options directive, but there's no way to re-enable
them on a specific "bind" line. This patch simply provides the option
to re-enable them. Note that the flag is inverted because tickets are
enabled by default and the no-tls-ticket option sets the flag to
disable them.
2025-05-22 15:31:54 +02:00
Willy Tarreau
3494775a1f MINOR: ssl: support strict-sni in ssl-default-bind-options
Several users already reported that it would be nice to support
strict-sni in ssl-default-bind-options. However, in order to support
it, we also need an option to disable it.

This patch moves the setting of the option from the strict_sni field
to a flag in the ssl_options field so that it can be inherited from
the default bind options, and adds a new "no-strict-sni" directive to
allow to disable it on a specific "bind" line.

The test file "del_ssl_crt-list.vtc" which already tests both options
was updated to make use of the default option and the no- variant to
confirm everything continues to work.
2025-05-22 15:31:54 +02:00
Christopher Faulet
7244f16ac4 MINOR: promex: Add agent check status/code/duration metrics
In the Prometheus exporter, the last health check status is already exposed,
with its code and duration in seconds. The server status is also exposed.
But the information about the agent check are not available. It is not
really handy because when a server status is changed because of the agent,
it is not obvious by looking to the Prometheus metrics. Indeed, the server
may reported as DOWN for instance, while the health check status still
reports a success. Being able to get the agent status in that case could be
valuable.

So now, the last agent check status is exposed, with its code and duration
in seconds. Following metrics can be grabbe now:

  * haproxy_server_agent_status
  * haproxy_server_agent_code
  * haproxy_server_agent_duration_seconds

Note that unlike the other metrics, no per-backend aggregated metric is
exposed.

This patch is related to issue #2983.
2025-05-22 09:50:10 +02:00
Willy Tarreau
0ac41ff97e [RELEASE] Released version 3.2-dev17
Released version 3.2-dev17 with the following main changes :
    - DOC: configuration: explicit multi-choice on bind shards option
    - BUG/MINOR: sink: detect and warn when using "send-proxy" options with ring servers
    - BUG/MEDIUM: peers: also limit the number of incoming updates
    - MEDIUM: hlua: Add function to change the body length of an HTTP Message
    - BUG/MEDIUM: stconn: Disable 0-copy forwarding for filters altering the payload
    - BUG/MINOR: h3: don't insert more than one Host header
    - BUG/MEDIUM: h1/h2/h3: reject forbidden chars in the Host header field
    - DOC: config: properly index "table and "stick-table" in their section
    - DOC: management: change reference to configuration manual
    - BUILD: debug: mark ha_crash_now() as attribute(noreturn)
    - IMPORT: slz: avoid multiple shifts on 64-bits
    - IMPORT: slz: support crc32c for lookup hash on sse4 but only if requested
    - IMPORT: slz: use a better hash for machines with a fast multiply
    - IMPORT: slz: fix header used for empty zlib message
    - IMPORT: slz: silence a build warning on non-x86 non-arm
    - BUG/MAJOR: leastconn: do not loop forever when facing saturated servers
    - BUG/MAJOR: queue: properly keep count of the queue length
    - BUG/MINOR: quic: fix crash on quic_conn alloc failure
    - BUG/MAJOR: leastconn: never reuse the node after dropping the lock
    - MINOR: acme: renewal notification over the dpapi sink
    - CLEANUP: quic: Useless BIO_METHOD initialization
    - MINOR: quic: Add useful error traces about qc_ssl_sess_init() failures
    - MINOR: quic: Allow the use of the new OpenSSL 3.5.0 QUIC TLS API (to be completed)
    - MINOR: quic: implement all remaining callbacks for OpenSSL 3.5 QUIC API
    - MINOR: quic: OpenSSL 3.5 internal QUIC custom extension for transport parameters reset
    - MINOR: quic: OpenSSL 3.5 trick to support 0-RTT
    - DOC: update INSTALL for QUIC with OpenSSL 3.5 usages
    - DOC: management: update 'acme status'
    - BUG/MEDIUM: wdt: always ignore the first watchdog wakeup
    - CLEANUP: wdt: clarify the comments on the common exit path
    - BUILD: ssl: avoid possible printf format warning in traces
    - BUILD: acme: fix build issue on 32-bit archs with 64-bit time_t
    - DOC: management: precise some of the fields of "show servers conn"
    - BUG/MEDIUM: mux-quic: fix BUG_ON() on rxbuf alloc error
    - DOC: watchdog: update the doc to reflect the recent changes
    - BUG/MEDIUM: acme: check if acme domains are configured
    - BUG/MINOR: acme: fix formatting issue in error and logs
    - EXAMPLES: lua: avoid screen refresh effect in "trisdemo"
    - CLEANUP: quic: remove unused cbuf module
    - MINOR: quic: move function to check stream type in utils
    - MINOR: quic: refactor handling of streams after MUX release
    - MINOR: quic: add some missing includes
    - MINOR: quic: adjust quic_conn-t.h include list
    - CLEANUP: cfgparse: alphabetically sort the global keywords
    - MINOR: glitches: add global setting "tune.glitches.kill.cpu-usage"
2025-05-21 15:56:06 +02:00
Willy Tarreau
a1577a89a0 MINOR: glitches: add global setting "tune.glitches.kill.cpu-usage"
It was mentioned during the development of glitches that it would be
nice to support not killing misbehaving connections below a certain
CPU usage so that poor implementations that routinely misbehave without
impact are not killed. This is now possible by setting a CPU usage
threshold under which we don't kill them via this parameter. It defaults
to zero so that we continue to kill them by default.
2025-05-21 15:47:42 +02:00
Willy Tarreau
eee57b4d3f CLEANUP: cfgparse: alphabetically sort the global keywords
The global keywords table was no longer sorted at all, let's fix it to
ease spotting the searched ones.
2025-05-21 15:47:42 +02:00
Amaury Denoyelle
00d90e8839 MINOR: quic: adjust quic_conn-t.h include list
Adjust include list in quic_conn-t.h. This file is included in many QUIC
source, so it is useful to keep as lightweight as possible. Note that
connection/QUIC MUX are transformed into forward declaration for better
layer separation.
2025-05-21 14:44:27 +02:00
Amaury Denoyelle
01e3b2119a MINOR: quic: add some missing includes
Insert some missing includes statement in QUIC source files. This was
detected after the next commit which adjust the include list used in
quic_conn-t.h file.
2025-05-21 14:44:27 +02:00
Amaury Denoyelle
f286288471 MINOR: quic: refactor handling of streams after MUX release
quic-conn layer has to handle itself STREAM frames after MUX release. If
the stream was already seen, it is probably only a retransmitted frame
which can be safely ignored. For other streams, an active closure may be
needed.

Thus it's necessary that quic-conn layer knows the highest stream ID
already handled by the MUX after its release. Previously, this was done
via <nb_streams> member array in quic-conn structure.

Refactor this by replacing <nb_streams> by two members called
<stream_max_uni>/<stream_max_bidi>. Indeed, it is unnecessary for
quic-conn layer to monitor locally opened uni streams, as the peer
cannot by definition emit a STREAM frame on it. Also, bidirectional
streams are always opened by the remote side.

Previously, <nb_streams> were set by quic-stream layer. Now,
<stream_max_uni>/<stream_max_bidi> members are only set one time, just
prior to QUIC MUX release. This is sufficient as quic-conn do not use
them if the MUX is available.

Note that previously, IDs were used relatively to their type, thus
incremented by 1, after shifting the original value. For simplification,
use the plain stream ID, which is incremented by 4.
2025-05-21 14:26:45 +02:00
Amaury Denoyelle
07d41a043c MINOR: quic: move function to check stream type in utils
Move general function to check if a stream is uni or bidirectional from
QUIC MUX to quic_utils module. This should prevent unnecessary include
of QUIC MUX header file in other sources.
2025-05-21 14:17:41 +02:00
Amaury Denoyelle
cf45bf1ad8 CLEANUP: quic: remove unused cbuf module
Cbuf are not used anymore. Remove the related source and header files,
as well as include statements in the rest of QUIC source files.
2025-05-21 14:16:37 +02:00
Baptiste Assmann
b437094853 EXAMPLES: lua: avoid screen refresh effect in "trisdemo"
In current version of the game, there is a "screen refresh" effect: the
screen is cleared before being re-drawn.
I moved the clear right after the connection is opened and removed it
from rendering time.
2025-05-21 12:00:53 +02:00
William Lallemand
8b121ab6f7 BUG/MINOR: acme: fix formatting issue in error and logs
Stop emitting \n in errmsg for intermediate error messages, this was
emitting multiline logs and was returning to a new line in the middle of
sentences.

We don't need to emit them in acme_start_task() since the errmsg is
ouput in a send_log which already contains a \n or on the CLI which
also emits it.
2025-05-21 11:41:28 +02:00
William Lallemand
156f4bd7a6 BUG/MEDIUM: acme: check if acme domains are configured
When starting the ACME task with a ckch_conf which does not contain the
domains, the ACME task would segfault because it will try to dereference
a NULL in this case.

The patch fix the issue by emitting a warning when no domains are
configured. It's not done at configuration parsing because it is not
easy to emit the warning because there are is no callback system which
give access to the whole ckch_conf once a line is parsed.

No backport needed.
2025-05-21 11:41:28 +02:00
Willy Tarreau
f5ed309449 DOC: watchdog: update the doc to reflect the recent changes
The watchdog was improved and fixed a few months ago, but the doc had
not been updated to reflect this. That's now done.
2025-05-21 11:34:55 +02:00
Amaury Denoyelle
e399daa67e BUG/MEDIUM: mux-quic: fix BUG_ON() on rxbuf alloc error
RX buffer allocation has been reworked in current dev tree. The
objective is to support multiple buffers per QCS to improve upload
throughput.

RX buffer allocation failure is handled simply : the whole connection is
closed. This is done via qcc_set_error(), with INTERNAL_ERROR as error
code. This function contains a BUG_ON() to ensure it is called only one
time per connection instance.

On RX buffer alloc failure, the aformentioned BUG_ON() crashes due to a
double invokation of qcc_set_error(). First by qcs_get_rxbuf(), and
immediately after it by qcc_recv(), which is the caller of the previous
one. This regression was introduced by the following commit.

  60f64449fbba7bb6e351e8343741bb3c960a2e6d
  MAJOR: mux-quic: support multiple QCS RX buffers

To fix this, simply remove qcc_set_error() invocation in
qcs_get_rxbuf(). On buffer alloc failture, qcc_recv() is responsible to
set the error.

This does not need to be backported.
2025-05-21 11:33:00 +02:00
Willy Tarreau
5c628d4e09 DOC: management: precise some of the fields of "show servers conn"
As reported in issue #2970, the output of "show servers conn" is not
clear. It was essentially meant as a debugging tool during some changes
to idle connections management, but if some users want to monitor or
graph them, more info is needed. The doc mentions the currently known
list of fields, and reminds that this output is not meant to be stable
over time, but as long as it does not change, it can provide some useful
metrics to some users.
2025-05-21 10:45:07 +02:00
Willy Tarreau
4b52d5e406 BUILD: acme: fix build issue on 32-bit archs with 64-bit time_t
The build failed on mips32 with a 64-bit time_t here:

  https://github.com/haproxy/haproxy/actions/runs/15150389164/job/42595310111

Let's just turn the "remain" variable used to show the remaining time
into a more portable ullong and use %llu for all format specifiers,
since long remains limited to 32-bit on 32-bit archs.

No backport needed.
2025-05-21 10:18:47 +02:00
Willy Tarreau
09d4c9519e BUILD: ssl: avoid possible printf format warning in traces
When building on MIPS-32 with gcc-9.5 and glibc-2.31, I got this:

  src/ssl_trace.c: In function 'ssl_trace':
  src/ssl_trace.c:118:42: warning: format '%ld' expects argument of type 'long int', but argument 3 has type 'ssize_t' {aka 'const int'} [-Wformat=]
    118 |     chunk_appendf(&trace_buf, " : size=%ld", *size);
        |                                        ~~^   ~~~~~
        |                                          |   |
        |                                          |   ssize_t {aka const int}
        |                                          long int
        |                                        %d

Let's just cast the type. No backport needed.
2025-05-21 10:01:14 +02:00
Willy Tarreau
3b2fb5cc15 CLEANUP: wdt: clarify the comments on the common exit path
The condition in which we reach the check for ha_panic() and
ha_stuck_warning() are not super clear, let's reformulate them.
2025-05-20 16:37:06 +02:00
Willy Tarreau
0a8bfb5b90 BUG/MEDIUM: wdt: always ignore the first watchdog wakeup
With commit a06c215f08 ("MEDIUM: wdt: always make the faulty thread
report its own warnings"), when the TH_FL_STUCK flag was flipped on,
we'd then go to the panic code instead of giving a second chance like
before the commit. This can trigger rare cases that only happen with
moderate loads like was addressed by commit 24ce001771 ("BUG/MEDIUM:
wdt: fix the stuck detection for warnings"). This is in fact due to
the loss of the common "goto update_and_leave" that used to serve
both the warning code and the flag setting for probation, and it's
apparently what hit Christian in issue #2980.

Let's make sure we exit naturally when turning the bit on for the
first time. Let's also update the confusing comment at the end of
the check that was left over by latest change.

Since the first commit was backported to 3.1, this commit should be
backported there as well.
2025-05-20 16:37:03 +02:00
William Lallemand
dcdf27af70 DOC: management: update 'acme status'
Update the 'acme status' section with the "Stopped" status and fix the
description.
2025-05-20 16:08:57 +02:00
Frederic Lecaille
bbe302087c DOC: update INSTALL for QUIC with OpenSSL 3.5 usages
Update the QUIC sections which mention the OpenSSL library use cases.
2025-05-20 15:00:06 +02:00
Frederic Lecaille
08eee0d9cf MINOR: quic: OpenSSL 3.5 trick to support 0-RTT
For an unidentified reason, SSL_do_hanshake() succeeds at its first call when 0-RTT
is enabled for the connection. This behavior looks very similar by the one encountered
by AWS-LC stack. That said, it was documented by AWS-LC. This issue leads the
connection to stop sending handshake packets after having release the handshake
encryption level. In fact, no handshake packets could even been sent leading
the handshake to always fail.

To fix this, this patch simulates a "handshake in progress" state waiting
for the application level read secret to be established by the TLS stack.
This may happen only after the QUIC listener has completed/confirmed the handshake
upon handshake CRYPTO data receipt from the peer.
2025-05-20 15:00:06 +02:00
Frederic Lecaille
849a3af14e MINOR: quic: OpenSSL 3.5 internal QUIC custom extension for transport parameters reset
A QUIC must sent its transport parameter using a TLS custom extention. This
extension is reset by SSL_set_SSL_CTX(). It can be restored calling
quic_ssl_set_tls_cbs() (which calls SSL_set_quic_tls_cbs()).
2025-05-20 15:00:06 +02:00
Frederic Lecaille
b3ac1a636c MINOR: quic: implement all remaining callbacks for OpenSSL 3.5 QUIC API
The quic_conn struct is modified for two reasons. The first one is to store
the encoded version of the local tranport parameter as this is done for
USE_QUIC_OPENSSL_COMPAT. Indeed, the local transport parameter "should remain
valid until after the parameters have been sent" as mentionned by
SSL_set_quic_tls_cbs(3) manual. In our case, the buffer is a static buffer
attached to the quic_conn object. qc_ssl_set_quic_transport_params() function
whose role is to call SSL_set_tls_quic_transport_params() (aliased by
SSL_set_quic_transport_params() to set these local tranport parameter into
the TLS stack from the buffer attached to the quic_conn struct.

The second quic_conn struct modification is the addition of the  new ->prot_level
(SSL protection level) member added to the quic_conn struct to store "the most
recent write encryption level set via the OSSL_FUNC_SSL_QUIC_TLS_yield_secret_fn
callback (if it has been called)" as mentionned by SSL_set_quic_tls_cbs(3) manual.

This patches finally implements the five remaining callacks to make the haproxy
QUIC implementation work.

OSSL_FUNC_SSL_QUIC_TLS_crypto_send_fn() (ha_quic_ossl_crypto_send) is easy to
implement. It calls ha_quic_add_handshake_data() after having converted
qc->prot_level TLS protection level value to the correct ssl_encryption_level_t
(boringSSL API/quictls) value.

OSSL_FUNC_SSL_QUIC_TLS_crypto_recv_rcd_fn() (ha_quic_ossl_crypto_recv_rcd())
provide the non-contiguous addresses to the TLS stack, without releasing
them.

OSSL_FUNC_SSL_QUIC_TLS_crypto_release_rcd_fn() (ha_quic_ossl_crypto_release_rcd())
release these non-contiguous buffer relying on the fact that the list of
encryption level (qc->qel_list) is correctly ordered by SSL protection level
secret establishements order (by the TLS stack).

OSSL_FUNC_SSL_QUIC_TLS_yield_secret_fn() (ha_quic_ossl_got_transport_params())
is a simple wrapping function over ha_quic_set_encryption_secrets() which is used
by boringSSL/quictls API.

OSSL_FUNC_SSL_QUIC_TLS_got_transport_params_fn() (ha_quic_ossl_got_transport_params())
role is to store the peer received transport parameters. It simply calls
quic_transport_params_store() and set them into the TLS stack calling
qc_ssl_set_quic_transport_params().

Also add some comments for all the OpenSSL 3.5 QUIC API callbacks.

This patch have no impact on the other use of QUIC API provided by the others TLS
stacks.
2025-05-20 15:00:06 +02:00
Frederic Lecaille
dc6a3c329a MINOR: quic: Allow the use of the new OpenSSL 3.5.0 QUIC TLS API (to be completed)
This patch allows the use of the new OpenSSL 3.5.0 QUIC TLS API when it is
available and detected at compilation time. The detection relies on the presence of the
OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_SEND macro from openssl-compat.h. Indeed this
macro is defined by OpenSSL since 3.5.0 version. It is not defined by quictls.
This helps in distinguishing these two TLS stacks. When the detection succeeds,
HAVE_OPENSSL_QUIC is also defined by openssl-compat.h. Then, this is this new macro
which is used to detect the availability of the new OpenSSL 3.5.0 QUIC TLS API.

Note that this detection is done only if USE_QUIC_OPENSSL_COMPAT is not asked.
So, USE_QUIC_OPENSSL_COMPAT and HAVE_OPENSSL_QUIC are exclusive.

At the same location, from openssl-compat.h, ssl_encryption_level_t enum is
defined. This enum was defined by quictls and expansively used by the haproxy
QUIC implementation. SSL_set_quic_transport_params() is replaced by
SSL_set_quic_tls_transport_params. SSL_set_quic_early_data_enabled() (quictls) is also replaced
by SSL_set_quic_tls_early_data_enabled() (OpenSSL). SSL_quic_read_level() (quictls)
is not defined by OpenSSL. It is only used by the traces to log the current
TLS stack decryption level (read). A macro makes it return -1 which is an
usused values.

The most of the differences between quictls and OpenSSL QUI APIs are in quic_ssl.c
where some callbacks must be defined for these two APIs. This is why this
patch modifies quic_ssl.c to define an array of OSSL_DISPATCH structs: <ha_quic_dispatch>.
Each element of this arry defines a callback. So, this patch implements these
six callabcks:

  - ha_quic_ossl_crypto_send()
  - ha_quic_ossl_crypto_recv_rcd()
  - ha_quic_ossl_crypto_release_rcd()
  - ha_quic_ossl_yield_secret()
  - ha_quic_ossl_got_transport_params() and
  - ha_quic_ossl_alert().

But at this time, these implementations which must return an int return 0 interpreted
as a failure by the OpenSSL QUIC API, except for ha_quic_ossl_alert() which
is implemented the same was as for quictls. The five remaining functions above
will be implemented by the next patches to come.

ha_quic_set_encryption_secrets() and ha_quic_add_handshake_data() have been moved
to be defined for both quictls and OpenSSL QUIC API.

These callbacks are attached to the SSL objects (sessions) calling qc_ssl_set_cbs()
new function. This latter callback the correct function to attached the correct
callbacks to the SSL objects (defined by <ha_quic_method> for quictls, and
<ha_quic_dispatch> for OpenSSL).

The calls to SSL_provide_quic_data() and SSL_process_quic_post_handshake()
have been also disabled. These functions are not defined by OpenSSL QUIC API.
At this time, the functions which call them are still defined when HAVE_OPENSSL_QUIC
is defined.
2025-05-20 15:00:06 +02:00
Frederic Lecaille
894595b711 MINOR: quic: Add useful error traces about qc_ssl_sess_init() failures
There were no traces to diagnose qc_ssl_sess_init() failures from QUIC traces.
This patch add calls to TRACE_DEVEL() into qc_ssl_sess_init() and its caller
(qc_alloc_ssl_sock_ctx()). This was useful at least to diagnose SSL context
initialization failures when porting QUIC to the new OpenSSL 3.5 QUIC API.

Should be easily backported as far as 2.6.
2025-05-20 15:00:06 +02:00
Frederic Lecaille
a2822b1776 CLEANUP: quic: Useless BIO_METHOD initialization
This code is there from QUIC implementation start. It was supposed to
initialize <ha_quic_meth> as a BIO_METHOD static object. But this
BIO_METHOD is not used at all!

Should be backported as far as 2.6 to help integrate the next patches to come.
2025-05-20 15:00:06 +02:00
William Lallemand
e803385a6e MINOR: acme: renewal notification over the dpapi sink
Output a sink message when the certificate was renewed by the ACME
client.

The message is emitted on the "dpapi" sink, and ends by \n\0.
Since the message contains this binary character, the right -0 parameter
must be used when consulting the sink over the CLI:

Example:

	$ echo "show events dpapi -nw -0" | socat -t9999 /tmp/haproxy.sock -
	<0>2025-05-19T15:56:23.059755+02:00 acme newcert foobar.pem.rsa\n\0

When used with the master CLI, @@1 should be used instead of @1 in order
to keep the connection to the worker.

Example:

	$ echo "@@1 show events dpapi -nw -0" | socat -t9999 /tmp/master.sock -
	<0>2025-05-19T15:56:23.059755+02:00 acme newcert foobar.pem.rsa\n\0
2025-05-19 16:07:25 +02:00
Willy Tarreau
99d6c889d0 BUG/MAJOR: leastconn: never reuse the node after dropping the lock
On ARM with 80 cores and a single server, it's sometimes possible to see
a segfault in fwlc_get_next_server() around 600-700k RPS. It seldom
happens as well on x86 with 128 threads with the same config around 1M
rps. It turns out that in fwlc_get_next_server(), before calling
fwlc_srv_reposition(), we have to drop the lock and that one takes it
back again.

The problem is that anything can happen to our node during this time,
and it can be freed. Then when continuing our work, we later iterate
over it and its next to find a node with an acceptable key, and by
doing so we can visit either uninitialized memory or simply nodes that
are no longer in the tree.

A first attempt at fixing this consisted in artificially incrementing
the elements count before dropping the lock, but that turned out to be
even worse because other threads could loop forever on such an element
looking for an entry that does not exist. Maintaining a separate
refcount didn't work well either, and it required to deal with the
memory release while dropping it, which is really not convenient.

Here we're taking a different approach consisting in simply not
trusting this node anymore and going back to the beginning of the
loop, as is done at a few other places as well. This way we can
safely ignore the possibly released node, and the test runs reliably
both on the arm and the x86 platforms mentioned above. No performance
regression was observed either, likely because this operation is quite
rare.

No backport is needed since this appeared with the leastconn rework
in 3.2.
2025-05-19 16:05:03 +02:00
Amaury Denoyelle
d358da4d83 BUG/MINOR: quic: fix crash on quic_conn alloc failure
If there is an alloc failure during qc_new_conn(), cleaning is done via
quic_conn_release(). However, since the below commit, an unchecked
dereferencing of <qc.path> is performed in the latter.

  e841164a4402118bd7b2e2dc2b5068f21de5d9d2
  MINOR: quic: account for global congestion window

To fix this, simply check <qc.path> before dereferencing it in
quic_conn_release(). This is safe as it is properly initialized to NULL
on qc_new_conn() first stage.

This does not need to be backported.
2025-05-19 11:03:48 +02:00
Willy Tarreau
099c1b2442 BUG/MAJOR: queue: properly keep count of the queue length
The queue length was moved to its own variable in commit 583303c48
("MINOR: proxies/servers: Calculate queueslength and use it."), however a
few places were missed in pendconn_unlink() and assign_server_and_queue()
resulting in never decreasing counts on aborted streams. This was
reproduced when injecting more connections than the total backend
could stand in TCP mode and letting some of them time out in the
queue. No backport is needed, this is only 3.2.
2025-05-17 10:46:10 +02:00
Willy Tarreau
6be02d1c6e BUG/MAJOR: leastconn: do not loop forever when facing saturated servers
Since commit 9fe72bba3 ("MAJOR: leastconn; Revamp the way servers are
ordered."), there's no way to escape the loop visiting the mt_list heads
in fwlc_get_next_server if all servers in the list are saturated,
resulting in a watchdog panic. It can be reproduced with this config
and injecting with more than 2 concurrent conns:

    balance leastconn
    server s1 127.0.0.1:8000 maxconn 1
    server s2 127.0.0.1:8000 maxconn 1

Here we count the number of saturated servers that were encountered, and
escape the loop once the number of remaining servers exceeds the number
of saturated ones. No backport is needed since this arrived in 3.2.
2025-05-17 10:44:36 +02:00
Willy Tarreau
ccc65012d3 IMPORT: slz: silence a build warning on non-x86 non-arm
Building with clang 16 on MIPS64 yields this warning:

  src/slz.c:931:24: warning: unused function 'crc32_uint32' [-Wunused-function]
  static inline uint32_t crc32_uint32(uint32_t data)
                         ^

Let's guard it using UNALIGNED_LE_OK which is the only case where it's
used. This saves us from introducing a possibly non-portable attribute.

This is libslz upstream commit f5727531dba8906842cb91a75c1ffa85685a6421.
2025-05-16 16:43:53 +02:00
Willy Tarreau
31ca29eee1 IMPORT: slz: fix header used for empty zlib message
Calling slz_rfc1950_finish() without emitting any data would result in
incorrectly emitting a gzip header (rfc1952) instead of a zlib header
(rfc1950) due to a copy-paste between the two wrappers. The impact is
almost inexistent since the zlib format is almost never used in this
context, and compressing totally empty messages is quite rare as well.
Let's take this opportunity for fixing another mistake on an RFC number
in a comment.

This is slz upstream commit 7f3fce4f33e8c2f5e1051a32a6bca58e32d4f818.
2025-05-16 16:43:53 +02:00
Willy Tarreau
411b04c7d3 IMPORT: slz: use a better hash for machines with a fast multiply
The current hash involves 3 simple shifts and additions so that it can
be mapped to a multiply on architecures having a fast multiply. This is
indeed what the compiler does on x86_64. A large range of values was
scanned to try to find more optimal factors on machines supporting such
a fast multiply, and it turned out that new factor 0x1af42f resulted in
smoother hashes that provided on average 0.4% better compression on both
the Silesia corpus and an mbox file composed of very compressible emails
and uncompressible attachments. It's even slightly better than CRC32C
while being faster on Skylake. This patch enables this factor on archs
with a fast multiply.

This is slz upstream commit 82ad1e75c13245a835c1c09764c89f2f6e8e2a40.
2025-05-16 16:43:53 +02:00
Willy Tarreau
248bbec83c IMPORT: slz: support crc32c for lookup hash on sse4 but only if requested
If building for sse4 and USE_CRC32C_HASH is defined, then we can use
crc32c to calculate the lookup hash. By default we don't do it because
even on skylake it's slower than the current hash, which only involves
a short multiply (~5% slower). But the gains are marginal (0.3%).

This is slz upstream commit 44ae4f3f85eb275adba5844d067d281e727d8850.

Note: this is not used by default and only merged in order to avoid
divergence between the code bases.
2025-05-16 16:43:53 +02:00
Willy Tarreau
ea1b70900f IMPORT: slz: avoid multiple shifts on 64-bits
On 64-bit platforms, disassembling the code shows that send_huff() performs
a left shift followed by a right one, which are the result of integer
truncation and zero-extension caused solely by using different types at
different levels in the call chain. By making encode24() take a 64-bit
int on input and send_huff() take one optionally, we can remove one shift
in the hot path and gain 1% performance without affecting other platforms.

This is slz upstream commit fd165b36c4621579c5305cf3bb3a7f5410d3720b.
2025-05-16 16:43:53 +02:00
Willy Tarreau
0a91c6dcae BUILD: debug: mark ha_crash_now() as attribute(noreturn)
Building on MIPS64 with clang16 incorrectly reports some uninitialized
value warnings in stats-proxy.c due to some calls to ABORT_NOW() where
the compiler didn't know the code wouldn't return. Let's properly mark
the function as noreturn, and take this opportunity for also marking it
unused to avoid possible warnings depending on the build options (if
ABORT_NOW is not used). No backport needed though it will not harm.
2025-05-16 16:43:53 +02:00
William Lallemand
1eebf98952 DOC: management: change reference to configuration manual
Since e24b77e7 ('DOC: config: move the extraneous sections out of the
"global" definition') the ACME section of the configuration manual was
move from 3.13 to 12.8.

Change the reference to that section in "acme renew".
2025-05-16 16:01:43 +02:00
Willy Tarreau
81e46be026 DOC: config: properly index "table and "stick-table" in their section
Tim reported in issue #2953 that "stick-table" and "table" were not
indexed as keywords. The issue was the indent level. Also let's make
sure to put a box around the "store" arguments as well.
2025-05-16 15:37:03 +02:00
Willy Tarreau
df00164fdd BUG/MEDIUM: h1/h2/h3: reject forbidden chars in the Host header field
In continuation with 9a05c1f574 ("BUG/MEDIUM: h2/h3: reject some
forbidden chars in :authority before reassembly") and the discussion
in issue #2941, @DemiMarie rightfully suggested that Host should also
be sanitized, because it is sometimes used in concatenation, such as
this:

    http-request set-url https://%[req.hdr(host)]%[pathq]

which was proposed as a workaround for h2 upstream servers that require
:authority here:

    https://www.mail-archive.com/haproxy@formilux.org/msg43261.html

The current patch then adds the same check for forbidden chars in the
Host header, using the same function as for the patch above, since in
both cases we validate the host:port part of the authority. This way
we won't reconstruct ambiguous URIs by concatenating Host and path.

Just like the patch above, this can be backported afer a period of
observation.
2025-05-16 15:13:17 +02:00
Willy Tarreau
b84762b3e0 BUG/MINOR: h3: don't insert more than one Host header
Let's make sure we drop extraneous Host headers after having compared
them. That also works when :authority was already present. This way,
like for h1 and h2, we only keep one copy of it, while still making
sure that Host matches :authority. This way, if a request has both
:authority and Host, only one Host header will be produced (from
:authority). Note that due to the different organization of the code
and wording along the evolving RFCs, here we also check that all
duplicates are identical, while h2 ignores them as per RFC7540, but
this will be re-unified later.

This should be backported to stable versions, at least 2.8, though
thanks to the existing checks the impact is probably nul.
2025-05-16 15:13:17 +02:00
Christopher Faulet
f45a632bad BUG/MEDIUM: stconn: Disable 0-copy forwarding for filters altering the payload
It is especially a problem with Lua filters, but it is important to disable
the 0-copy forwarding if a filter alters the payload, or at least to be able
to disable it. While the filter is registered on the data filtering, it is
not an issue (and it is the common case) because, there is now way to
fast-forward data at all. But it may be an issue if a filter decides to
alter the payload and to unregister from data filtering. In that case, the
0-copy forwarding can be re-enabled in a hardly precdictable state.

To fix the issue, a SC flags was added to do so. The HTTP compression filter
set it and lua filters too if the body length is changed (via
HTTPMessage.set_body_len()).

Note that it is an issue because of a bad design about the HTX. Many info
about the message are stored in the HTX structure itself. It must be
refactored to move several info to the stream-endpoint descriptor. This
should ease modifications at the stream level, from filter or a TCP/HTTP
rules.

This should be backported as far as 3.0. If necessary, it may be backported
on lower versions, as far as 2.6. In that case, it must be reviewed and
adapted.
2025-05-16 15:11:37 +02:00
Christopher Faulet
94055a5e73 MEDIUM: hlua: Add function to change the body length of an HTTP Message
There was no function for a lua filter to change the body length of an HTTP
Message. But it is mandatory to be able to alter the message payload. It is
not possible update to directly update the message headers because the
internal state of the message must also be updated accordingly.

It is the purpose of HTTPMessage.set_body_len() function. The new body
length myst be passed as argument. If it is an integer, the right
"Content-Length" header is set. If the "chunked" string is used, it forces
the message to be chunked-encoded and in that case the "Transfer-Encoding"
header.

This patch should fix the issue #2837. It could be backported as far as 2.6.
2025-05-16 14:34:12 +02:00
Willy Tarreau
f2d7aa8406 BUG/MEDIUM: peers: also limit the number of incoming updates
There's a configurable limit to the number of messages sent to a
peer (tune.peers.max-updates-at-once), but this one is not applied to
the receive side. While it can usually be OK with default settings,
setups involving a large tune.bufsize (1MB and above) regularly
experience high latencies and even watchdogs during reloads because
the full learning process sends a lot of data that manages to fill
the entire buffer, and due to the compactness of the protocol, 1MB
of buffer can contain more than 100k updates, meaning taking locks
etc during this time, which is not workable.

Let's make sure the receiving side also respects the max-updates-at-once
setting. For this it counts incoming updates, and refrains from
continuing once the limit is reached. It's a bit tricky to do because
after receiving updates we still have to send ours (and possibly some
ACKs) so we cannot just leave the loop.

This issue was reported on 3.1 but it should progressively be backported
to all versions having the max-updates-at-once option available.
2025-05-15 16:57:21 +02:00
Aurelien DARRAGON
098a5e5c0b BUG/MINOR: sink: detect and warn when using "send-proxy" options with ring servers
using "send-proxy" or "send-proxy-v2" option on a ring server is not
relevant nor supported. Worse, on 2.4 it causes haproxy process to
crash as reported in GH #2965.

Let's be more explicit about the fact that this keyword is not supported
under "ring" context by ignoring the option and emitting a warning message
to inform the user about that.

Ideally, we should do the same for peers and log servers. The proper way
would be to check servers options during postparsing but we currently lack
proper cross-type server postparsing hooks. This will come later and thus
will give us a chance to perform the compatibilty checks for server
options depending on proxy type. But for now let's simply fix the "ring"
case since it is the only one that's known to cause a crash.

It may be backported to all stable versions.
2025-05-15 16:18:31 +02:00
Basha Mougamadou
824bb93e18 DOC: configuration: explicit multi-choice on bind shards option
From the documentation, this wasn't clear enough that shards should
be followed by one of the options number / by-thread / by-group.
Align it with existing options in documentation so that it becomes
more explicit.
2025-05-14 19:41:38 +02:00
Willy Tarreau
17df04ff09 [RELEASE] Released version 3.2-dev16
Released version 3.2-dev16 with the following main changes :
    - BUG/MEDIUM: mux-quic: fix crash on invalid fctl frame dereference
    - DEBUG: pool: permit per-pool UAF configuration
    - MINOR: acme: add the global option 'acme.scheduler'
    - DEBUG: pools: add a new integrity mode "backup" to copy the released area
    - MEDIUM: sock-inet: re-check IPv6 connectivity every 30s
    - BUG/MINOR: ssl: doesn't fill conf->crt with first arg
    - BUG/MINOR: ssl: prevent multiple 'crt' on the same ssl-f-use line
    - BUG/MINOR: ssl/ckch: always free() the previous entry during parsing
    - MINOR: tools: ha_freearray() frees an array of string
    - BUG/MINOR: ssl/ckch: always ha_freearray() the previous entry during parsing
    - MINOR: ssl/ckch: warn when the same keyword was used twice
    - BUG/MINOR: threads: fix soft-stop without multithreading support
    - BUG/MINOR: tools: improve parse_line()'s robustness against empty args
    - BUG/MINOR: cfgparse: improve the empty arg position report's robustness
    - BUG/MINOR: server: dont depend on proxy for server cleanup in srv_drop()
    - BUG/MINOR: server: perform lbprm deinit for dynamic servers
    - MINOR: http: add a function to validate characters of :authority
    - BUG/MEDIUM: h2/h3: reject some forbidden chars in :authority before reassembly
    - MINOR: quic: account Tx data per stream
    - MINOR: mux-quic: account Rx data per stream
    - MINOR: quic: add stream format for "show quic"
    - MINOR: quic: display QCS info on "show quic stream"
    - MINOR: quic: display stream age
    - BUG/MINOR: cpu-topo: fix group-by-cluster policy for disordered clusters
    - MINOR: cpu-topo: add a new "group-by-ccx" CPU policy
    - MINOR: cpu-topo: provide a function to sort clusters by average capacity
    - MEDIUM: cpu-topo: change "performance" to consider per-core capacity
    - MEDIUM: cpu-topo: change "efficiency" to consider per-core capacity
    - MEDIUM: cpu-topo: prefer grouping by CCX for "performance" and "efficiency"
    - MEDIUM: config: change default limits to 1024 threads and 32 groups
    - BUG/MINOR: hlua: Fix Channel:data() and Channel:line() to respect documentation
    - DOC: config: Fix a typo in the "term_events" definition
    - BUG/MINOR: spoe: Don't report error on applet release if filter is in DONE state
    - BUG/MINOR: mux-spop: Don't report error for stream if ACK was already received
    - BUG/MINOR: mux-spop: Make the demux stream ID a signed integer
    - BUG/MINOR: mux-spop: Don't open new streams for SPOP connection on error
    - MINOR: mux-spop: Don't set SPOP connection state to FRAME_H after ACK parsing
    - BUG/MEDIUM: mux-spop: Remove frame parsing states from the SPOP connection state
    - BUG/MEDIUM: mux-spop: Properly handle CLOSING state
    - BUG/MEDIUM: spop-conn: Report short read for partial frames payload
    - BUG/MEDIUM: mux-spop: Properly detect truncated frames on demux to report error
    - BUG/MEDIUM: mux-spop; Don't report a read error if there are pending data
    - DEBUG: mux-spop: Review some trace messages to adjust the message or the level
    - DOC: config: move address formats definition to section 2
    - DOC: config: move stick-tables and peers to their own section
    - DOC: config: move the extraneous sections out of the "global" definition
    - CI: AWS-LC(fips): enable unit tests
    - CI: AWS-LC: enable unit tests
    - CI: compliance: limit run on forks only to manual + cleanup
    - CI: musl: enable unit tests
    - CI: QuicTLS (weekly): limit run on forks only to manual dispatch
    - CI: WolfSSL: enable unit tests
2025-05-14 17:01:46 +02:00
Ilia Shipitsin
12de9ecce5 CI: WolfSSL: enable unit tests
Run the new make unit-tests on the CI.
2025-05-14 17:00:31 +02:00
Ilia Shipitsin
75a1e40501 CI: QuicTLS (weekly): limit run on forks only to manual dispatch 2025-05-14 17:00:31 +02:00
Ilia Shipitsin
a8b1b08fd7 CI: musl: enable unit tests
Run the new make unit-tests on the CI.
2025-05-14 17:00:31 +02:00
Ilia Shipitsin
01225f9aa5 CI: compliance: limit run on forks only to manual + cleanup 2025-05-14 17:00:31 +02:00
Ilia Shipitsin
61b30a09c0 CI: AWS-LC: enable unit tests
Run the new make unit-tests on the CI.
2025-05-14 17:00:31 +02:00
Ilia Shipitsin
944a96156e CI: AWS-LC(fips): enable unit tests
Run the new make unit-tests on the CI.
2025-05-14 17:00:31 +02:00
Willy Tarreau
e24b77e765 DOC: config: move the extraneous sections out of the "global" definition
Due to some historic mistakes that have spread to newly added sections,
a number of of recently added small sections found themselves described
under section 3 "global parameters" which is specific to "global" section
keywords. This is highly confusing, especially given that sections 3.1,
3.2, 3.3 and 3.10 directly start with keywords valid in the global section,
while others start with keywords that describe a new section.

Let's just create a new chapter "12. other sections" and move them all
there. 3.10 "HTTPclient tuning" however was moved to 3.4 as it's really
a definition of the global options assigned to the HTTP client. The
"programs" that are going away in 3.3 were moved at the end to avoid a
renumbering later.

Another nice benefit is that it moves a lot of text that was previously
keeping the global and proxies sections apart.
2025-05-14 16:08:02 +02:00
Willy Tarreau
da67a89f30 DOC: config: move stick-tables and peers to their own section
As suggested by Tim in issue #2953, stick-tables really deserve their own
section to explain the configuration. And peers have to move there as well
since they're totally dedicated to stick-tables.

Now we introduce a new section "Stick-tables and Peers", explaining the
concepts, and under which there is one subsection for stick-tables
configuration and one for the peers (which mostly keeps the existing
peers section).
2025-05-14 16:08:02 +02:00
Willy Tarreau
423dffa308 DOC: config: move address formats definition to section 2
Section 2 describes the config file format, variables naming etc, so
there's no reason why the address format used in this file should be
in a separate section, let's bring it into section 2 as well.
2025-05-14 16:08:02 +02:00
Christopher Faulet
e2ae8a74e8 DEBUG: mux-spop: Review some trace messages to adjust the message or the level
Some trace messages were not really accurrate, reporting a CLOSED connection
while only an error was reported on it. In addition, an TRACE_ERROR() was
used to report a short read on HELLO/DISCONNECT frames header. But it is not
an error. a TRACE_DEVEL() should be used instead.

This patch could be backported to 3.1 to ease future backports.
2025-05-14 11:52:10 +02:00
Christopher Faulet
6e46f0bf93 BUG/MEDIUM: mux-spop; Don't report a read error if there are pending data
When an read error is detected, no error must be reported on the SPOP
connection is there are still some data to parse. It is important to be sure
to process all data before reporting the error and be sure to not truncate
received frames. However, we must also take care to handle short read case
to not wait data that will never be received.

This patch must be backported to 3.1.
2025-05-14 11:51:58 +02:00
Christopher Faulet
16314bb93c BUG/MEDIUM: mux-spop: Properly detect truncated frames on demux to report error
There was no test in the demux part to detect truncated frames and to report
an error at the connection level. The SPOP streams were properly switch to
half-closed state. But waiting the associated SPOE applets were woken up and
released, the SPOP connection could be woken up several times for nothing. I
never triggered the watchdog in that case, but it is not excluded.

Now, at the end of the demux function, if a specific test was added to
detect truncated frames to report an error and close the connection.

This patch must be backported to 3.1.
2025-05-14 11:47:41 +02:00
Christopher Faulet
71feb49a9f BUG/MEDIUM: spop-conn: Report short read for partial frames payload
When a frame was not fully received, a short read must be reported on the
SPOP connection to help the demux to handle truncated frames. This was
performed for frames truncated on the header part but not on the payload
part. It is now properly detected.

This patch must be backported to 3.1.
2025-05-14 09:20:10 +02:00
Christopher Faulet
ddc5f8d92e BUG/MEDIUM: mux-spop: Properly handle CLOSING state
The CLOSING state was not handled at all by the SPOP multiplexer while it is
mandatory when a DISCONNECT frame was sent and the mux should wait for the
DISCONNECT frame in reply from the agent. Thanks to this patch, it should be
fixed.

In addition, if an error occurres during the AGENT HELLO frame parsing, the
SPOP connection is no longer switched to CLOSED state and remains in ERROR
state instead. It is important to be able to send the DISCONNECT frame to
the agent instead of closing the TCP connection immediately.

This patch depends on following commits:

  * BUG/MEDIUM: mux-spop: Remove frame parsing states from the SPOP connection state
  * MINOR: mux-spop: Don't set SPOP connection state to FRAME_H after ACK parsing
  * BUG/MINOR: mux-spop: Don't open new streams for SPOP connection on error
  * BUG/MINOR: mux-spop: Make the demux stream ID a signed integer

All the series must be backported to 3.1.
2025-05-14 09:14:12 +02:00
Christopher Faulet
a3940614c2 BUG/MEDIUM: mux-spop: Remove frame parsing states from the SPOP connection state
SPOP_CS_FRAME_H and SPOP_CS_FRAME_P states, that were used to handle frame
parsing, were removed. The demux process now relies on the demux stream ID
to know if it is waiting for the frame header or the frame
payload. Concretly, when the demux stream ID is not set (dsi == -1), the
demuxer is waiting for the next frame header. Otherwise (dsi >= 0), it is
waiting for the frame payload. It is especially important to be able to
properly handle DISCONNECT frames sent by the agents.

SPOP_CS_RUNNING state is introduced to know the hello handshake was finished
and the SPOP connection is able to open SPOP streams and exchange NOTIFY/ACK
frames with the agents.

It depends on the following fixes:

  * MINOR: mux-spop: Don't set SPOP connection state to FRAME_H after ACK parsing
  * BUG/MINOR: mux-spop: Make the demux stream ID a signed integer

This change will be mandatory for the next fix. It must be backported to 3.1
with the commits above.
2025-05-13 19:51:40 +02:00
Christopher Faulet
6b0f7de4e3 MINOR: mux-spop: Don't set SPOP connection state to FRAME_H after ACK parsing
After the ACK frame was parsed, it is useless to set the SPOP connection
state to SPOP_CS_FRAME_H state because this will be automatically handled by
the demux function. If it is not an issue, but this will simplify changes
for the next commit.
2025-05-13 19:51:40 +02:00
Christopher Faulet
197eaaadfd BUG/MINOR: mux-spop: Don't open new streams for SPOP connection on error
Till now, only SPOP connections fully closed or those with a TCP connection on
error were concerned. But available streams could be reported for SPOP
connections in error or closing state. But in these states, no NOTIFY frames
will be sent and no ACK frames will be parsed. So, no new SPOP streams should be
opened.

This patch should be backported to 3.1.
2025-05-13 19:51:40 +02:00
Christopher Faulet
cbc10b896e BUG/MINOR: mux-spop: Make the demux stream ID a signed integer
The demux stream ID of a SPOP connection, used when received frames are
parsed, must be a signed integer because it is set to -1 when the SPOP
connection is initialized. It will be important for the next fix.

This patch must be backported to 3.1.
2025-05-13 19:51:40 +02:00
Christopher Faulet
6d68beace5 BUG/MINOR: mux-spop: Don't report error for stream if ACK was already received
When a SPOP connection was closed or was in error, an error was
systematically reported on all its SPOP streams. However, SPOP streams that
already received their ACK frame must be excluded. Otherwise if an agent
sends a ACK and close immediately, the ACK will be ignored because the SPOP
stream will handle the error first.

This patch must be backported to 3.1.
2025-05-13 19:51:40 +02:00
Christopher Faulet
1cd30c998b BUG/MINOR: spoe: Don't report error on applet release if filter is in DONE state
When the SPOE applet was released, if a SPOE filter context was still
attached to it, an error was reported to the filter. However, there is no
reason to report an error if the ACK message was already received. Because
of this bug, if the ACK message is received and the SPOE connection is
immediately closed, this prevents the ACK message to be processed.

This patch should be backported to 3.1.
2025-05-13 19:51:40 +02:00
Christopher Faulet
dcce02d6ed DOC: config: Fix a typo in the "term_events" definition
A space was missing before the colon.
2025-05-13 19:51:40 +02:00
Christopher Faulet
a5de0e1595 BUG/MINOR: hlua: Fix Channel:data() and Channel:line() to respect documentation
When the channel API was revisted, the both functions above was added. An
offset can be passed as argument. However, this parameter could be reported
to be out of range if there was not enough input data was received yet. It
is an issue, especially with a tcp rule, because more data could be
received. If an error is reported too early, this prevent the rule to be
reevaluated later. In fact, an error should only be reported if the offset
is part of the output data.

Another issue is about the conditions to report 'nil' instead of an empty
string. 'nil' was reported when no data was found. But it is not aligned
with the documentation. 'nil' must only be returned if no more data cannot
be received and there is no input data at all.

This patch should fix the issue #2716. It should be backported as far as 2.6.
2025-05-13 19:51:40 +02:00
Willy Tarreau
e049bd00ab MEDIUM: config: change default limits to 1024 threads and 32 groups
A test run on a dual-socket EPYC 9845 (2x160 cores) showed that we'll
be facing new limits during the lifetime of 3.2 with our current 16
groups and 256 threads max:

  $ cat test.cfg
  global
      cpu-policy perforamnce

  $ ./haproxy -dc -c -f test.cfg
  ...
  Thread CPU Bindings:
    Tgrp/Thr  Tid        CPU set
    1/1-32    1-32       32: 0-15,320-335
    2/1-32    33-64      32: 16-31,336-351
    3/1-32    65-96      32: 32-47,352-367
    4/1-32    97-128     32: 48-63,368-383
    5/1-32    129-160    32: 64-79,384-399
    6/1-32    161-192    32: 80-95,400-415
    7/1-32    193-224    32: 96-111,416-431
    8/1-32    225-256    32: 112-127,432-447

Raising the default limit to 1024 threads and 32 groups is sufficient
to buy us enough margin for a long time (hopefully, please don't laugh,
you, reader from the future):

  $ ./haproxy -dc -c -f test.cfg
  ...
  Thread CPU Bindings:
    Tgrp/Thr  Tid        CPU set
    1/1-32    1-32       32: 0-15,320-335
    2/1-32    33-64      32: 16-31,336-351
    3/1-32    65-96      32: 32-47,352-367
    4/1-32    97-128     32: 48-63,368-383
    5/1-32    129-160    32: 64-79,384-399
    6/1-32    161-192    32: 80-95,400-415
    7/1-32    193-224    32: 96-111,416-431
    8/1-32    225-256    32: 112-127,432-447
    9/1-32    257-288    32: 128-143,448-463
    10/1-32   289-320    32: 144-159,464-479
    11/1-32   321-352    32: 160-175,480-495
    12/1-32   353-384    32: 176-191,496-511
    13/1-32   385-416    32: 192-207,512-527
    14/1-32   417-448    32: 208-223,528-543
    15/1-32   449-480    32: 224-239,544-559
    16/1-32   481-512    32: 240-255,560-575
    17/1-32   513-544    32: 256-271,576-591
    18/1-32   545-576    32: 272-287,592-607
    19/1-32   577-608    32: 288-303,608-623
    20/1-32   609-640    32: 304-319,624-639

We can change this default now because it has no functional effect
without any configured cpu-policy, so this will only be an opt-in
and it's better to do it now than to have an effect during the
maintenance phase. A tiny effect is a doubling of the number of
pool buckets and stick-table shards internally, which means that
aside slightly reducing contention in these areas, a dump of tables
can enumerate keys in a different order (hence the adjustment in the
vtc).

The only really visible effect is a slightly higher static memory
consumption (29->35 MB on a small config), but that difference
remains even with 50k servers so that's pretty much acceptable.

Thanks to Erwan Velu for the quick tests and the insights!
2025-05-13 18:15:33 +02:00
Willy Tarreau
158da59c34 MEDIUM: cpu-topo: prefer grouping by CCX for "performance" and "efficiency"
Most of the time, machines made of multiple CPU types use the same L3
for them, and grouping CPUs by frequencies to form groups doesn't bring
any value and on the opposite can impair the incoming connection balancing.
This choice of grouping by cluster was made in order to constitute a good
choice on homogenous machines as well, so better rely on the per-CCX
grouping than the per-cluster one in this case. This will create less
clusters on machines where it counts without affecting other ones.

It doesn't seem necessary to change anything for the "resource" policy
since it selects a single cluster.
2025-05-13 16:48:30 +02:00
Willy Tarreau
70b0dd6b0f MEDIUM: cpu-topo: change "efficiency" to consider per-core capacity
This is similar to the previous change to the "performance" policy but
it applies to the "efficiency" one. Here we're changing the sorting
method to sort CPU clusters by average per-CPU capacity, and we evict
clusters whose per-CPU capacity is above 125% of the previous one.
Per-core capacity allows to detect discrepancies between CPU cores,
and to continue to focus on efficient ones as a priority.
2025-05-13 16:48:30 +02:00
Willy Tarreau
6c88e27cf4 MEDIUM: cpu-topo: change "performance" to consider per-core capacity
Running the "performance" policy on highly heterogenous systems yields
bad choices when there are sufficiently more small than big cores,
and/or when there are multiple cluster types, because on such setups,
the higher the frequency, the lower the number of cores, despite small
differences in frequencies. In such cases, we quickly end up with
"performance" only choosing the small or the medium cores, which is
contrary to the original intent, which was to select performance cores.
This is what happens on boards like the Orion O6 for example where only
the 4 medium cores and 2 big cores are choosen, evicting the 2 biggest
cores and the 4 smallest ones.

Here we're changing the sorting method to sort CPU clusters by average
per-CPU capacity, and we evict clusters whose per-CPU capacity falls
below 80% of the previous one. Per-core capacity allows to detect
discrepancies between CPU cores, and to continue to focus on high
performance ones as a priority.
2025-05-13 16:48:30 +02:00
Willy Tarreau
5ab2c815f1 MINOR: cpu-topo: provide a function to sort clusters by average capacity
The current per-capacity sorting function acts on a whole cluster, but
in some setups having many small cores and few big ones, it becomes
easy to observe an inversion of metrics where the many small cores show
a globally higher total capacity than the few big ones. This does not
necessarily fit all use cases. Let's add new a function to sort clusters
by their per-cpu average capacity to cover more use cases.
2025-05-13 16:48:30 +02:00
Willy Tarreau
01df98adad MINOR: cpu-topo: add a new "group-by-ccx" CPU policy
This cpu-policy will only consider CCX and not clusters. This makes
a difference on machines with heterogenous CPUs that generally share
the same L3 cache, where it's not desirable to create multiple groups
based on the CPU types, but instead create one with the different CPU
types. The variants "group-by-2/3/4-ccx" have also been added.

Let's also add some text explaining the difference between cluster
and CCX.
2025-05-13 16:48:30 +02:00
Willy Tarreau
33d8b006d4 BUG/MINOR: cpu-topo: fix group-by-cluster policy for disordered clusters
Some (rare) boards have their clusters in an erratic order. This is
the case for the Radxa Orion O6 where one of the big cores appears as
CPU0 due to booting from it, then followed by the small cores, then the
medium cores, then the remaining big cores. This results in clusters
appearing this order: 0,2,1,0.

The core in cpu_policy_group_by_cluster() expected ordered clusters,
and performs ordered comparisons to decide whether a CPU's cluster has
already been taken care of. On the board above this doesn't work, only
clusters 0 and 2 appear and 1 is skipped.

Let's replace the cluster number comparison with a cpuset to record
which clusters have been taken care of. Now the groups properly appear
like this:

  Tgrp/Thr  Tid        CPU set
  1/1-2     1-2        2: 0,11
  2/1-4     3-6        4: 1-4
  3/1-6     7-12       6: 5-10

No backport is needed, this is purely 3.2.
2025-05-13 16:48:30 +02:00
Amaury Denoyelle
f3b9676416 MINOR: quic: display stream age
Add a field to save the creation date of qc_stream_desc instance. This
is useful to display QUIC stream age in "show quic stream" output.
2025-05-13 15:44:22 +02:00
Amaury Denoyelle
dbf07c754e MINOR: quic: display QCS info on "show quic stream"
Complete stream output for "show quic" by displaying information from
its upper QCS. Note that QCS may be NULL if already released, so a
default output is also provided.
2025-05-13 15:43:28 +02:00
Amaury Denoyelle
cbadfa0163 MINOR: quic: add stream format for "show quic"
Add a new format for "show quic" command labelled as "stream". This is
an equivalent of "show sess", dedicated to the QUIC stack. Each active
QUIC streams are listed on a line with their related infos.

The main objective of this command is to ensure there is no freeze
streams remaining after a transfer.
2025-05-13 15:41:51 +02:00
Amaury Denoyelle
1ccede211c MINOR: mux-quic: account Rx data per stream
Add counters to measure Rx buffers usage per QCS. This reused the newly
defined bdata_ctr type already used for Tx accounting.

Note that for now, <tot> value of bdata_ctr is not used. This is because
it is not easy to account for data accross contiguous buffers.

These values are displayed both on log/traces and "show quic" output.
2025-05-13 15:41:51 +02:00
Amaury Denoyelle
a1dc9070e7 MINOR: quic: account Tx data per stream
Add accounting at qc_stream_desc level to be able to report the number
of allocated Tx buffers and the sum of their data. This represents data
ready for emission or already emitted and waiting on ACK.

To simplify this accounting, a new counter type bdata_ctr is defined in
quic_utils.h. This regroups both buffers and data counter, plus a
maximum on the buffer value.

These values are now displayed on QCS info used both on logline and
traces, and also on "show quic" output.
2025-05-13 15:41:41 +02:00
Willy Tarreau
9a05c1f574 BUG/MEDIUM: h2/h3: reject some forbidden chars in :authority before reassembly
As discussed here:
   https://github.com/httpwg/http2-spec/pull/936
   https://github.com/haproxy/haproxy/issues/2941

It's important to take care of some special characters in the :authority
pseudo header before reassembling a complete URI, because after assembly
it's too late (e.g. the '/'). This patch does this, both for h2 and h3.

The impact on H2 was measured in the worst case at 0.3% of the request
rate, while the impact on H3 is around 1%, but H3 was about 1% faster
than H2 before and is now on par.

It may be backported after a period of observation, and in this case it
relies on this previous commit:

   MINOR: http: add a function to validate characters of :authority

Thanks to @DemiMarie for reviving this topic in issue #2941 and bringing
new potential interesting cases.
2025-05-12 18:02:47 +02:00
Willy Tarreau
ebab479cdf MINOR: http: add a function to validate characters of :authority
As discussed here:
  https://github.com/httpwg/http2-spec/pull/936
  https://github.com/haproxy/haproxy/issues/2941

It's important to take care of some special characters in the :authority
pseudo header before reassembling a complete URI, because after assembly
it's too late (e.g. the '/').

This patch adds a specific function which was checks all such characters
and their ranges on an ist, and benefits from modern compilers
optimizations that arrange the comparisons into an evaluation tree for
faster match. That's the version that gave the most consistent performance
across various compilers, though some hand-crafted versions using bitmaps
stored in register could be slightly faster but super sensitive to code
ordering, suggesting that the results might vary with future compilers.
This one takes on average 1.2ns per character at 3 GHz (3.6 cycles per
char on avg). The resulting impact on H2 request processing time (small
requests) was measured around 0.3%, from 6.60 to 6.618us per request,
which is a bit high but remains acceptable given that the test only
focused on req rate.

The code was made usable both for H2 and H3.
2025-05-12 18:02:47 +02:00
Aurelien DARRAGON
c40d6ac840 BUG/MINOR: server: perform lbprm deinit for dynamic servers
Last commit 7361515 ("BUG/MINOR: server: dont depend on proxy for server
cleanup in srv_drop()") introduced a regression because the lbprm
server_deinit is not evaluated anymore with dynamic servers, possibly
resulting in a memory leak.

To fix the issue, in addition to free_proxy(), the server deinit check
should be manually performed in cli_parse_delete_server() as well.

No backport needed.
2025-05-12 16:29:36 +02:00
Aurelien DARRAGON
736151556c BUG/MINOR: server: dont depend on proxy for server cleanup in srv_drop()
In commit b5ee8bebfc ("MINOR: server: always call ssl->destroy_srv when
available"), we made it so srv_drop() doesn't depend on proxy to perform
server cleanup.

It turns out this is now mandatory, because during deinit, free_proxy()
can occur before the final srv_drop(). This is the case when using Lua
scripts for instance.

In 2a9436f96 ("MINOR: lbprm: Add method to deinit server and proxy") we
added a freeing check under srv_drop() that depends on the proxy.
Because of that UAF may occur during deinit when using a Lua script that
manipulate server objects.

To fix the issue, let's perform the lbprm server deinit logic under
free_proxy() directly, where the DEINIT server hooks are evaluated.

Also, to prevent similar bugs in the future, let's explicitly document
in srv_drop() that server cleanups should assume that the proxy may
already be freed.

No backport needed unless 2a9436f96 is.
2025-05-12 16:17:26 +02:00
Willy Tarreau
be4d816be2 BUG/MINOR: cfgparse: improve the empty arg position report's robustness
OSS Fuzz found that the previous fix ebb19fb367 ("BUG/MINOR: cfgparse:
consider the special case of empty arg caused by \x00") was incomplete,
as the output can sometimes be larger than the input (due to variables
expansion) in which case the work around to try to report a bad arg will
fail. While the parse_line() function has been made more robust now in
order to avoid this condition, let's fix the handling of this special
case anyway by just pointing to the beginning of the line if the supposed
error location is out of the line's buffer.

All details here:
   https://oss-fuzz.com/testcase-detail/5202563081502720

No backport is needed unless the fix above is backported.
2025-05-12 16:11:15 +02:00
Willy Tarreau
2b60e54fb1 BUG/MINOR: tools: improve parse_line()'s robustness against empty args
The fix in 10e6d0bd57 ("BUG/MINOR: tools: only fill first empty arg when
not out of range") was not that good. It focused on protecting against
<arg> becoming out of range to detect we haven't emitted anything, but
it's not the right way to detect this. We're always maintaining arg_start
as a copy of outpos, and that later one is incremented when emitting a
char, so instead of testing args[arg] against out+arg_start, we should
instead check outpos against arg_start, thereby eliminating the <out>
offset and the need to access args[]. This way we now always know if
we've emitted an empty arg without dereferencing args[].

There's no need to backport this unless the fix above is also backported.
2025-05-12 16:11:15 +02:00
Aurelien DARRAGON
7d057e56af BUG/MINOR: threads: fix soft-stop without multithreading support
When thread support is disabled ("USE_THREAD=" or "USE_THREAD=0" when
building), soft-stop doesn't work as haproxy never ends after stopping
the proxies.

This used to work fine in the past but suddenly stopped working with
ef422ced91 ("MEDIUM: thread: make stopping_threads per-group and add
stopping_tgroups") because the "break;" instruction under the stopping
condition is never executed when support for multithreading is disabled.

To fix the issue, let's add an "else" block to run the "break;"
instruction when USE_THREAD is not defined.

It should be backported up to 2.8
2025-05-12 14:18:39 +02:00
William Lallemand
8b0d1a4113 MINOR: ssl/ckch: warn when the same keyword was used twice
When using a crt-list or a crt-store, keywords mentionned twice on the
same line overwritte the previous value.

This patch emits a warning when the same keyword is found another time
on the same line.
2025-05-09 19:18:38 +02:00
William Lallemand
9c0c05b7ba BUG/MINOR: ssl/ckch: always ha_freearray() the previous entry during parsing
The ckch_conf_parse() function is the generic function which parses
crt-store keywords from the crt-store section, and also from a
crt-list.

When having multiple time the same keyword, a leak of the previous
value happens. This patch ensure that the previous value is always
freed before overwriting it.

This is the same problem as the previous "BUG/MINOR: ssl/ckch: always
free() the previous entry during parsing" patch, however this one
applies on PARSE_TYPE_ARRAY_SUBSTR.

No backport needed.
2025-05-09 19:16:02 +02:00
William Lallemand
96b1f1fd26 MINOR: tools: ha_freearray() frees an array of string
ha_freearray() is a new function which free() an array of strings
terminated by a NULL entry.

The pointer to the array will be free and set to NULL.
2025-05-09 19:12:05 +02:00
William Lallemand
311e0aa5c7 BUG/MINOR: ssl/ckch: always free() the previous entry during parsing
The ckch_conf_parse() function is the generic function which parses
crt-store keywords from the crt-store section, and also from a crt-list.

When having multiple time the same keyword, a leak of the previous value
happens. This patch ensure that the previous value is always freed
before overwriting it.

This patch should be backported as far as 3.0.
2025-05-09 19:01:28 +02:00
William Lallemand
9ce3fb35a2 BUG/MINOR: ssl: prevent multiple 'crt' on the same ssl-f-use line
The 'ssl-f-use' implementation doesn't prevent to have multiple time the
'crt' keyword, which overwrite the previous value. Letting users think
that is it possible to use multiple certificates on the same line, which
is not the case.

This patch emits an alert when setting the 'crt' keyword multiple times
on the same ssl-f-use line.

Should fix issue #2966.

No backport needed.
2025-05-09 18:52:09 +02:00
William Lallemand
0c4abf5a22 BUG/MINOR: ssl: doesn't fill conf->crt with first arg
Commit c7f29afc ("MEDIUM: ssl: replace "crt" lines by "ssl-f-use"
lines") forgot to remove an the allocation of the crt field which was
done with the first argument.

Since ssl-f-use takes keywords, this would put the first keyword in
"crt" instead of the certificate name.
2025-05-09 18:23:06 +02:00
Willy Tarreau
8a96216847 MEDIUM: sock-inet: re-check IPv6 connectivity every 30s
IPv6 connectivity might start off (e.g. network not fully up when
haproxy starts), so for features like resolvers, it would be nice to
periodically recheck.

With this change, instead of having the resolvers code rely on a variable
indicating connectivity, it will now call a function that will check for
how long a connectivity check hasn't been run, and will perform a new one
if needed. The age was set to 30s which seems reasonable considering that
the DNS will cache results anyway. There's no saving in spacing it more
since the syscall is very check (just a connect() without any packet being
emitted).

The variables remain exported so that we could present them in show info
or anywhere else.

This way, "dns-accept-family auto" will now stay up to date. Warning
though, it does perform some caching so even with a refreshed IPv6
connectivity, an older record may be returned anyway.
2025-05-09 15:45:44 +02:00
Willy Tarreau
1404f6fb7b DEBUG: pools: add a new integrity mode "backup" to copy the released area
This way we can preserve the entire contents of the released area for
later inspection. This automatically enables comparison at reallocation
time as well (like "integrity" does). If used in combination with
integrity, the comparison is disabled but the check of non-corruption
of the area mangled by integrity is still operated.
2025-05-09 14:57:00 +02:00
William Lallemand
e7574cd5f0 MINOR: acme: add the global option 'acme.scheduler'
The automatic scheduler is useful but sometimes you don't want to use,
or schedule manually.

This patch adds an 'acme.scheduler' option in the global section, which
can be set to either 'auto' or 'off'. (auto is the default value)

This also change the ouput of the 'acme status' command so it does not
shows scheduled values. The state will be 'Stopped' instead of
'Scheduled'.
2025-05-09 14:00:39 +02:00
Willy Tarreau
0ae14beb2a DEBUG: pool: permit per-pool UAF configuration
The new MEM_F_UAF flag can be set just after a pool's creation to make
this pool UAF for debugging purposes. This allows to maintain a better
overall performance required to reproduce issues while still having a
chance to catch UAF. It will only be used by developers who will manually
add it to areas worth being inspected, though.
2025-05-09 13:59:02 +02:00
Amaury Denoyelle
14e4f2b811 BUG/MEDIUM: mux-quic: fix crash on invalid fctl frame dereference
Emission of flow-control frames have been recently modified. Now, each
frame is sent one by one, via a single entry list. If a failure occurs,
emission is interrupted and frame is reinserted into the original
<qcc.lfctl.frms> list.

This code is incorrect as it only checks if qcc_send_frames() returns an
error code to perform the reinsert operation. However, an error here
does not always mean that the frame was not properly emitted by lower
quic-conn layer. As such, an extra test LIST_ISEMPTY() must be performed
prior to reinsert the frame.

This bug would cause a heap overflow. Indeed, the reinsert frame would
be a random value. A crash would occur as soon as it would be
dereferenced via <qcc.lfctl.frms> list.

This was reproduced by issuing a POST with a big file and interrupt it
after just a few seconds. This results in a crash in about a third of
the tests. Here is an example command using ngtcp2 :

 $ ngtcp2-client -q --no-quic-dump --no-http-dump \
   -m POST -d ~/infra/html/1g 127.0.0.1 20443 "http://127.0.0.1:20443/post"

Heap overflow was detected via a BUG_ON() statement from qc_frm_free()
via qcc_release() caller :

  FATAL: bug condition "!((&((*frm)->reflist))->n == (&((*frm)->reflist)))" matched at src/quic_frame.c:1270

This does not need to be backported.
2025-05-09 11:07:11 +02:00
Willy Tarreau
3f9194bfc9 [RELEASE] Released version 3.2-dev15
Released version 3.2-dev15 with the following main changes :
    - BUG/MEDIUM: stktable: fix sc_*(<ctr>) BUG_ON() regression with ctx > 9
    - BUG/MINOR: acme/cli: don't output error on success
    - BUG/MINOR: tools: do not create an empty arg from trailing spaces
    - MEDIUM: config: warn about the consequences of empty arguments on a config line
    - MINOR: tools: make parse_line() provide hints about empty args
    - MINOR: cfgparse: visually show the input line on empty args
    - BUG/MINOR: tools: always terminate empty lines
    - BUG/MINOR: tools: make parseline report the required space for the trailing 0
    - DEBUG: threads: don't keep lock label "OTHER" in the per-thread history
    - DEBUG: threads: merge successive idempotent lock operations in history
    - DEBUG: threads: display held locks in threads dumps
    - BUG/MINOR: proxy: only use proxy_inc_fe_cum_sess_ver_ctr() with frontends
    - Revert "BUG/MEDIUM: mux-spop: Handle CLOSING state and wait for AGENT DISCONNECT frame"
    - MINOR: acme/cli: 'acme status' show the status acme-configured certificates
    - MEDIUM: acme/ssl: remove 'acme ps' in favor of 'acme status'
    - DOC: configuration: add "acme" section to the keywords list
    - DOC: configuration: add the "crt-store" keyword
    - BUG/MAJOR: queue: lock around the call to pendconn_process_next_strm()
    - MINOR: ssl: add filename and linenum for ssl-f-use errors
    - BUG/MINOR: ssl: can't use crt-store some certificates in ssl-f-use
    - BUG/MINOR: tools: only fill first empty arg when not out of range
    - MINOR: debug: bump the dump buffer to 8kB
    - MINOR: stick-tables: add "ipv4" as an alias for the "ip" type
    - MINOR: quic: extend return value during TP parsing
    - BUG/MINOR: quic: use proper error code on missing CID in TPs
    - BUG/MINOR: quic: use proper error code on invalid server TP
    - BUG/MINOR: quic: reject retry_source_cid TP on server side
    - BUG/MINOR: quic: use proper error code on invalid received TP value
    - BUG/MINOR: quic: fix TP reject on invalid max-ack-delay
    - BUG/MINOR: quic: reject invalid max_udp_payload size
    - BUG/MEDIUM: peers: hold the refcnt until updating ts->seen
    - BUG/MEDIUM: stick-tables: close a tiny race in __stksess_kill()
    - BUG/MINOR: cli: fix too many args detection for commands
    - MINOR: server: ensure server postparse tasks are run for dynamic servers
    - BUG/MEDIUM: stick-table: always remove update before adding a new one
    - BUG/MEDIUM: quic: free stream_desc on all data acked
    - BUG/MINOR: cfgparse: consider the special case of empty arg caused by \x00
    - DOC: config: recommend disabling libc-based resolution with resolvers
2025-05-09 10:51:30 +02:00
Willy Tarreau
4e20fab7ac DOC: config: recommend disabling libc-based resolution with resolvers
Using both libc and haproxy resolvers can lead to hard to diagnose issues
when their bevahiour diverges; recommend using only one type of resolver.

Should be backported to stable versions.

Link: https://www.mail-archive.com/haproxy@formilux.org/msg45663.html
Co-authored-by: Lukas Tribus <lukas@ltri.eu>
2025-05-09 10:31:39 +02:00
Willy Tarreau
ebb19fb367 BUG/MINOR: cfgparse: consider the special case of empty arg caused by \x00
The reporting of the empty arg location added with commit 08d3caf30
("MINOR: cfgparse: visually show the input line on empty args") falls
victim of a special case detected by OSS Fuzz:

     https://issues.oss-fuzz.com/issues/415850462

In short, making an argument start with "\x00" doesn't make it empty for
the parser, but still emits an empty string which is detected and
displayed. Unfortunately in this case the error pointer is not set so
the sanitization function crashes.

What we're doing in this case is that we fall back to the position of
the output argument as an estimate of where it was located in the input.
It's clearly inexact (quoting etc) but will still help the user locate
the problem.

No backport is needed unless the commit above is backported.
2025-05-09 10:01:44 +02:00
Amaury Denoyelle
3fdb039a99 BUG/MEDIUM: quic: free stream_desc on all data acked
The following patch simplifies qc_stream_desc_ack(). The qc_stream_desc
instance is not freed anymore, even if all data were acknowledged. As
implies by the commit message, the caller is responsible to perform this
cleaning operation.
  f4a83fbb14bdd14ed94752a2280a2f40c1b690d2
  MINOR: quic: do not remove qc_stream_desc automatically on ACK handling

However, despite the commit instruction, qc_stream_desc_free()
invokation was not moved in the caller. This commit fixes this by adding
it after stream ACK handling. This is performed only when a transfer is
completed : all data is acknowledged and qc_stream_desc has been
released by its MUX stream instance counterpart.

This bug may cause a significant increase in memory usage when dealing
with long running connection. However, there is no memory leak, as every
qc_stream_desc attached to a connection are finally freed when quic_conn
instance is released.

This must be backported up to 3.1.
2025-05-09 09:25:47 +02:00
Willy Tarreau
576e47fb9a BUG/MEDIUM: stick-table: always remove update before adding a new one
Since commit 388539faa ("MEDIUM: stick-tables: defer adding updates to a
tasklet"), between the entry creation and its arrival in the updates tree,
there is time for scheduling, and it now becomes possible for an stksess
entry to be requeued into the list while it's still in the tree as a remote
one. Only local updates were removed prior to being inserted. In this case
we would re-insert the entry, causing it to appear as the parent of two
distinct nodes or leaves, and to be visited from the first leaf during a
delete() after having already been removed and freed, causing a crash,
as Christian reported in issue #2959.

There's no reason to backport this as this appeared with the commit above
in 3.2-dev13.
2025-05-08 23:32:25 +02:00
Aurelien DARRAGON
f03e999912 MINOR: server: ensure server postparse tasks are run for dynamic servers
commit 29b76cae4 ("BUG/MEDIUM: server/log: "mode log" after server
keyword causes crash") introduced some postparsing checks/tasks for
server

Initially they were mainly meant for "mode log" servers postparsing, but
we already have a check dedicated to "tcp/http" servers (ie: only tcp
proto supported)

However when dynamic servers are added they bypass _srv_postparse() since
the REGISTER_POST_SERVER_CHECK() is only executed for servers defined in
the configuration.

To ensure consistency between dynamic and static servers, and ensure no
post-check init routine is missed, let's manually invoke _srv_postparse()
after creating a dynamic server added via the cli.
2025-05-08 02:03:50 +02:00
Aurelien DARRAGON
976e0bd32f BUG/MINOR: cli: fix too many args detection for commands
d3f928944 ("BUG/MINOR: cli: Issue an error when too many args are passed
for a command") added a new check to prevent the command to run when
too many arguments are provided. In this case an error is reported.

However it turns out this check (despite marked for backports) was
ineffective prior to 20ec1de21 ("MAJOR: cli: Refacor parsing and
execution of pipelined commands") as 'p' pointer was reset to the end of
the buffer before the check was executed.

Now since 20ec1de21, the check works, but we have another issue: we may
read past initialized bytes in the buffer because 'p' pointer is always
incremented in a while loop without checking if we increment it past 'end'
(This was detected using valgrind)

To fix the issue introduced by 20ec1de21, let's only increment 'p' pointer
if p < end.

For 3.2 this is it, now for older versions, since d3f928944 was marked for
backport, a sligthly different approach is needed:

 - conditional p increment must be done in the loop (as in this patch)
 - max arg check must moved above "fill unused slots" comment where p is
   assigned to the end of the buffer

This patch should be backported with d3f928944.
2025-05-08 02:03:43 +02:00
Willy Tarreau
0cee7b5b8d BUG/MEDIUM: stick-tables: close a tiny race in __stksess_kill()
It might be possible not to see the element in the tree, then not to
see it in the update list, thus not to take the lock before deleting.
But an element in the list could have moved to the tree during the
check, and be removed later without the updt_lock.

Let's delete prior to checking the presence in the tree to avoid
this situation. No backport is needed since this arrived in -dev13
with the update list.
2025-05-07 18:49:21 +02:00
Willy Tarreau
006a3acbde BUG/MEDIUM: peers: hold the refcnt until updating ts->seen
In peer_treat_updatemsg(), we call stktable_touch_remote() after
releasing the write lock on the TS, asking it to decrement the
refcnt, then we update ts->seen. Unfortunately this is racy and
causes the issue that Christian reported in issue #2959.

The sequence of events is very hard to trigger manually, but what happens
is the following:

 T1.  stktable_touch_remote(table, ts, 1);
      -> at this point the entry is in the mt_list, and the refcnt is zero.

      T2.  stktable_trash_oldest() or process_table_expire()
           -> these can run, because the refcnt is now zero.
              The entry is cleanly deleted and freed.

 T1.  HA_ATOMIC_STORE(&ts->seen, 1)
      -> we dereference freed memory.

A first attempt at a fix was made by keeping the refcnt held during
all the time the entry is in the mt_list, but this is expensive as
such entries cannot be purged, causing lots of skips during
trash_oldest_data(). This managed to trigger watchdogs, and was only
hiding the real cause of the problem.

The correct approach clearly is to maintain the ref_cnt until we
touch ->seen. That's what this patch does. It does not decrement
the refcnt, while calling stktable_touch_remote(), and does it
manually after touching ->seen. With this the problem is gone.

Note that a reproducer involves the following:
  - a config with 10 stick-ctr tracking the same table with a
    random key between 10M and 100M depending on the machine.
  - the expiration should be between 10 and 20s. http_req_cnt
    is stored and shared with the peers.
  - 4 total processes with such a config on the local machine,
    each corresponding to a different peer. 3 of the peers are
    bound to half of the cores (all threads) and share the same
    threads; the last process is bound to the other half with
    its own threads.
  - injecting at full load, ~256 conn, on the shared listening
    port. After ~2x expiration time to 1 minute the lone process
    should segfault in pools code due to a corrupted by_lru list.

This problem already exists in earlier versions but the race looks
narrower. Given how difficult it is to trigger on a given machine
in its current form, it's likely that it only happens once in a
while on stable branches. The fix must be backported wherever the
code is similar, and there's no hope to reproduce it to validate
the backport.

Thanks again to Christian for his amazing help!
2025-05-07 18:49:21 +02:00
Amaury Denoyelle
4bc7aa548a BUG/MINOR: quic: reject invalid max_udp_payload size
Add a checks on received max_udp_payload transport parameters. As
defined per RFC 9000, values below 1200 are invalid, and thus the
connection must be closed with TRANSPORT_PARAMETER_ERROR code.

Prior to this patch, an invalid value was silently ignored.

This should be backported up to 2.6. Note that is relies on previous
patch "MINOR: quic: extend return value on TP parsing".
2025-05-07 15:21:30 +02:00
Amaury Denoyelle
ffabfb0fc3 BUG/MINOR: quic: fix TP reject on invalid max-ack-delay
Checks are implemented on some received transport parameter values,
to reject invalid ones defined per RFC 9000. This is the case for
max_ack_delay parameter.

The check was not properly implemented as it only reject values strictly
greater than the limit set to 2^14. Fix this by rejecting values of 2^14
and above. Also, the proper error code TRANSPORT_PARAMETER_ERROR is now
set.

This should be backported up to 2.6. Note that is relies on previous
patch "MINOR: quic: extend return value on TP parsing".
2025-05-07 15:21:30 +02:00
Amaury Denoyelle
b60a17aad7 BUG/MINOR: quic: use proper error code on invalid received TP value
As per RFC 9000, checks must be implemented to reject invalid values for
received transport parameters. Such values are dependent on the
parameter type.

Checks were already implemented for ack_delay_exponent and
active_connection_id_limit, accordingly with the QUIC specification.
However, the connection was closed with an incorrect error code. Fix
this to ensure that TRANSPORT_PARAMETER_ERROR code is used as expected.

This should be backported up to 2.6. Note that is relies on previous
patch "MINOR: quic: extend return value on TP parsing".
2025-05-07 15:21:30 +02:00
Amaury Denoyelle
10f1f1adce BUG/MINOR: quic: reject retry_source_cid TP on server side
Close the connection on error if retry_source_connection_id transport
parameter is received. This is specified by RFC 9000 as this parameter
must not be emitted by a client. Previously, it was silently ignored.

This should be backported up to 2.6. Note that is relies on previous
patch "MINOR: quic: extend return value on TP parsing".
2025-05-07 15:21:30 +02:00
Amaury Denoyelle
a54fdd3d92 BUG/MINOR: quic: use proper error code on invalid server TP
This commit is similar to the previous one. It fixes the error code
reported when dealing with invalid received transport parameters. This
time, it handles reception of original_destination_connection_id,
preferred_address and stateless_reset_token which must not be emitted by
the client.

This should be backported up to 2.6. Note that is relies on previous
patch "MINOR: quic: extend return value on TP parsing".
2025-05-07 15:20:06 +02:00
Amaury Denoyelle
df6bd4909e BUG/MINOR: quic: use proper error code on missing CID in TPs
Handle missing received transport parameter value
initial_source_connection_id / original_destination_connection_id.
Previously, such case would result in an error reported via
quic_transport_params_store(), which triggers a TLS alert converted as
expected as a CONNECTION_CLOSE. The issue is that the error code
reported in the frame was incorrect.

Fix this by returning QUIC_TP_DEC_ERR_INVAL for such conditions. This is
directly handled via quic_transport_params_store() which set the proper
TRANSPORT_PARAMETER_ERROR code for the CONNECTION_CLOSE. However, no
error is reported so the SSL handshake is properly terminated without a
TLS alert. This is enough to ensure that the CONNECTION_CLOSE frame will
be emitted as expected.

This should be backported up to 2.6. Note that is relies on previous
patch "MINOR: quic: extend return value on TP parsing".
2025-05-07 15:20:06 +02:00
Amaury Denoyelle
294bf26c06 MINOR: quic: extend return value during TP parsing
Extend API used for QUIC transport parameter decoding. This is done via
the introduction of a dedicated enum to report the various error
condition detected. No functional change should occur with this patch,
as the only returned code is QUIC_TP_DEC_ERR_TRUNC, which results in the
connection closure via a TLS alert.

This patch will be necessary to properly reject transport parameters
with the proper CONNECTION_CLOSE error code. As such, it should be
backported up to 2.6 with the following series.
2025-05-07 15:19:52 +02:00
Willy Tarreau
46b5dcad99 MINOR: stick-tables: add "ipv4" as an alias for the "ip" type
However the doc purposely says the opposite, to encourage migrating away
from "ip". The goal is that in the future we change "ip" to mean "ipv6",
which seems to be what most users naturally expect. But we cannot break
configurations in the LTS version so for now "ipv4" is the alias.

The reason for not changing it in the table is that the type name is
used at a few places (look for "].kw"):
  - dumps
  - promex

We'd rather not change that output for 3.2, but only do it in 3.3.
This way, 3.2 can be made future-proof by using "ipv4" in the config
without any other side effect.

Please see github issue #2962 for updates on this transition.
2025-05-07 10:11:55 +02:00
Willy Tarreau
697a531516 MINOR: debug: bump the dump buffer to 8kB
Now with the improved backtraces, the lock history and details in the
mux layers, some dumps appear truncated or with some chars alone at
the beginning of the line. The issue is in fact caused by the limited
dump buffer size (2kB for stderr, 4kB for warning), that cannot hold
a complete trace anymore.

Let's jump bump them to 8kB, this will be plenty for a long time.
2025-05-07 10:02:58 +02:00
Willy Tarreau
10e6d0bd57 BUG/MINOR: tools: only fill first empty arg when not out of range
In commit 3f2c8af313 ("MINOR: tools: make parse_line() provide hints
about empty args") we've added the ability to record the position of
the first empty arg in parse_line(), but that check requires to
access the args[] array for the current arg, which is not valid in
case we stopped on too large an argument count. Let's just check the
arg's validity before doing so.

This was reported by OSS Fuzz:
  https://issues.oss-fuzz.com/issues/415850462

No backport is needed since this was in the latest dev branch.
2025-05-07 07:25:29 +02:00
William Lallemand
fbceabbccf BUG/MINOR: ssl: can't use crt-store some certificates in ssl-f-use
When declaring a certificate via the crt-store section, this certificate
can then be used 2 ways in a crt-list:
- only by using its name, without any crt-store options
- or by using the exact set of crt-list option that was defined in the
  crt-store

Since ssl-f-use is generating a crt-list, this is suppose to behave the
same. To achieve this, ckch_conf_parse() will parse the keywords related
to the ckch_conf on the ssl-f-use line and use ckch_conf_cmp() to
compare it to the previous declaration from the crt-store. This
comparaison is only done when any ckch_conf keyword are present.

However, ckch_conf_parse() was done for the crt-list, and the crt-list
does not use the "crt" parameter to declare the name of the certificate,
since it's the first element of the line. So when used with ssl-f-use,
ckch_conf_parse() will always see a "crt" keyword which is a ckch_conf
one, and consider that it will always need to have the exact same set of
paremeters when using the same crt in a crt-store and an ssl-f-use line.

So a simple configuration like this:

   crt-store web
     load crt "foo.com.crt" key "foo.com.key" alias "foo"

   frontend mysite
     bind :443 ssl
     ssl-f-use crt "@web/foo" ssl-min-ver TLSv1.2

Would lead to an error like this:

    config : '@web/foo' in crt-list '(null)' line 0, is already defined with incompatible parameters:
    - different parameter 'key' : previously 'foo.com.key' vs '(null)'

In order to fix the issue, this patch parses the "crt" parameter itself
for ssl-f-use instead of using ckch_conf_parse(), so the keyword would
never be considered as a ckch_conf keyword to compare.

This patch also take care of setting the CKCH_CONF_SET_CRTLIST flag only
if a ckch_conf keyword was found. This flag is used by ckch_conf_cmp()
to know if it has to compare or not.

No backport needed.
2025-05-06 21:36:29 +02:00
William Lallemand
b3b282d2ee MINOR: ssl: add filename and linenum for ssl-f-use errors
Fill cfg_crt_node with a filename and linenum so the post_section
callback can use it to emit errors.

This way the errors are emitted with the right filename and linenum
where ssl-f-use is used instead of (null):0
2025-05-06 21:36:29 +02:00
Willy Tarreau
99f5be5631 BUG/MAJOR: queue: lock around the call to pendconn_process_next_strm()
The extra call to pendconn_process_next_strm() made in commit cda7275ef5
("MEDIUM: queue: Handle the race condition between queue and dequeue
differently") was performed after releasing the server queue's lock,
which is incompatible with the calling convention for this function.
The result is random corruption of the server's streams list likely
due to picking old or incorrect pendconns from the queue, and in the
end infinitely looping on apparently already locked mt_list objects.
Just adding the lock fixes the problem.

It's very difficult to reproduce, it requires low maxconn values on
servers, stickiness on the servers (cookie), a long enough slowstart
(e.g. 10s), and regularly flipping servers up/down to re-trigger the
slowstart.

No backport is needed as this was only in 3.2.
2025-05-06 18:59:54 +02:00
William Lallemand
e035f0c48e DOC: configuration: add the "crt-store" keyword
Add the "crt-store" keyword with its argument in the "3.12" section, so
this could be detected by haproxy-dconv has a keyword and put in the
keywords list.

Must be backported as far as 3.0
2025-05-06 16:07:29 +02:00
William Lallemand
e516b14d36 DOC: configuration: add "acme" section to the keywords list
Add the "acme" keyword with its argument in the "3.13" section, so this
could be detected by haproxy-dconv has a keyword and put in the keywords
list.
2025-05-06 15:34:39 +02:00
William Lallemand
b7c4a68ecf MEDIUM: acme/ssl: remove 'acme ps' in favor of 'acme status'
Remove the 'acme ps' command which does not seem useful anymore with the
'acme status' command.

The big difference with the 'acme status' command is that it was only
displaying the running tasks instead of the status of all certificate.
2025-05-06 15:27:29 +02:00
William Lallemand
48f1ce77b7 MINOR: acme/cli: 'acme status' show the status acme-configured certificates
The "acme status" command, shows the status of every certificates
configured with ACME, not only the running task like "acme ps".

The IO handler loops on the ckch_store tree and outputs a line for each
ckch_store which has an acme section set. This is still done under the
ckch_store lock and doesn't support resuming when the buffer is full,
but we need to change that in the future.
2025-05-06 15:27:29 +02:00
Christopher Faulet
a3ce7d7772 Revert "BUG/MEDIUM: mux-spop: Handle CLOSING state and wait for AGENT DISCONNECT frame"
This reverts commit 53c3046898633e56f74f7f05fb38cabeea1c87a1.

This patch introduced a regression leading to a loop on the frames
demultiplexing because a frame may be ignore but not consumed.

But outside this regression that can be fixed, there is a design issue that
was not totally fixed by the patch above. The SPOP connection state is mixed
with the status of the frames demultiplexer and this needlessly complexify
the connection management. Instead of fixing the fix, a better solution is
to revert it to work a a proper solution.

For the record, the idea is to deal with the spop connection state onlu
using 'state' field and to introduce a new field to handle the frames
demultiplexer state. This should ease the closing state management.

Another issue that must be fixed. We must take care to not abort a SPOP
stream when an error is detected on a SPOP connection or when the connection
is closed, if the ACK frame was already received for this stream. It is not
a common case, but it can be solved by saving the last known stream ID that
recieved a ACK.

This patch must be backported if the commit above is backported.
2025-05-06 13:43:59 +02:00
Aurelien DARRAGON
b39825ee45 BUG/MINOR: proxy: only use proxy_inc_fe_cum_sess_ver_ctr() with frontends
proxy_inc_fe_cum_sess_ver_ctr() was implemented in 9969adbc
("MINOR: stats: add by HTTP version cumulated number of sessions and
requests")

As its name suggests, it is meant to be called for frontends, not backends

Also, in 9969adbc, when used under h1_init(), a precaution is taken to
ensure that the function is only called with frontends.

However, this precaution was not applied in h2_init() and qc_init().

Due to this, it remains possible to have proxy_inc_fe_cum_sess_ver_ctr()
being called with a backend proxy as parameter. While it did not cause
known issues so far, it is not expected and could result in bugs in the
future. Better fix this by ensuring the function is only called with
frontends.

It may be backported up to 2.8
2025-05-06 11:01:39 +02:00
Willy Tarreau
3bb6eea6d5 DEBUG: threads: display held locks in threads dumps
Based on the lock history, we can spot some locks that are still held
by checking the last operation that happened on them: if it's not an
unlock, then we know the lock is held. In this case we append the list
after "locked:" with their label and state like below:

  U:QUEUE S:IDLE_CONNS U:IDLE_CONNS R:TASK_WQ U:TASK_WQ S:QUEUE S:QUEUE S:QUEUE locked: QUEUE(S)
  S:IDLE_CONNS U:IDLE_CONNS S:TASK_RQ U:TASK_RQ S:QUEUE U:QUEUE S:IDLE_CONNS locked: IDLE_CONNS(S)
  R:TASK_WQ S:TASK_WQ R:TASK_WQ S:TASK_WQ R:TASK_WQ S:TASK_WQ R:TASK_WQ locked: TASK_WQ(R)
  W:STK_TABLE W:STK_TABLE_UPDT U:STK_TABLE_UPDT W:STK_TABLE W:STK_TABLE_UPDT U:STK_TABLE_UPDT W:STK_TABLE W:STK_TABLE_UPDT locked: STK_TABLE(W) STK_TABLE_UPDT(W)

The format is slightly different (label(status)) so as to easily
differentiate them visually from the history.
2025-05-06 05:20:37 +02:00
Willy Tarreau
feaac66b5e DEBUG: threads: merge successive idempotent lock operations in history
In order to make the lock history a bit more useful, let's try to merge
adjacent lock/unlock sequences that don't change anything for other
threads. For this we can replace the last unlock with the new operation
on the same label, and even just not store it if it was the same as the
one before the unlock, since in the end it's the same as if the unlock
had not been done.

Now loops that used to be filled with "R:LISTENER U:LISTENER" show more
useful info such as:

  S:IDLE_CONNS U:IDLE_CONNS S:PEER U:PEER S:IDLE_CONNS U:IDLE_CONNS R:LISTENER U:LISTENER
  U:STK_TABLE W:STK_SESS U:STK_SESS R:STK_TABLE U:STK_TABLE W:STK_SESS U:STK_SESS R:STK_TABLE
  R:STK_TABLE U:STK_TABLE W:STK_SESS U:STK_SESS W:STK_TABLE_UPDT U:STK_TABLE_UPDT S:PEER

It's worth noting that it can sometimes induce confusion when recursive
locks of the same label are used (a few exist on peers or stick-tables),
as in such a case the two operations would be needed. However these ones
are already undebuggable, so instead they will just have to be renamed
to make sure they use a distinct label.
2025-05-05 18:36:12 +02:00
Willy Tarreau
743dce95d2 DEBUG: threads: don't keep lock label "OTHER" in the per-thread history
Most threads are filled with "R:OTHER U:OTHER" in their history. Since
anything non-important can use other it's not observable but it pollutes
the history. Let's just drop OTHER entirely during the recording.
2025-05-05 18:10:57 +02:00
Willy Tarreau
1f51f1c816 BUG/MINOR: tools: make parseline report the required space for the trailing 0
The fix in commit 09a325a4de ("BUG/MINOR: tools: always terminate empty
lines") is insufficient. While it properly addresses the lack of trailing
zero, it doesn't account for it in the returned outlen that is used to
allocate a larger line. This happens at boot if the very first line of
the test file is exactly a sharp with nothing else. In this case it will
return a length 0 and the caller (parse_cfg()) will try to re-allocate an
entry of size zero and will fail, bailing out a lack of memory. This time
it should really be OK.

It doesn't need to be backported, unless the patch above would be.
2025-05-05 17:58:04 +02:00
Willy Tarreau
09a325a4de BUG/MINOR: tools: always terminate empty lines
Since latest commit 7e4a2f39ef ("BUG/MINOR: tools: do not create an empty
arg from trailing spaces"), an empty line will no longer produce an arg
and no longer append a trailing zero to them. This was not visible because
one is already present in the input string, however all the trailing args
are set to out+outpos-1, which now points one char before the buffer since
nothing was emitted, and was noticed by ASAN, and/or when parsing garbage.
Let's make sure to always emit the zero for empty lines as well to address
this issue. No backport is needed unless the patch above gets backported.
2025-05-05 17:33:22 +02:00
Willy Tarreau
08d3caf30e MINOR: cfgparse: visually show the input line on empty args
Now when an empty arg is found on a line, we emit the sanitized
input line and the position of the first empty arg so as to help
the user figure the cause (likely an empty environment variable).

Co-authored-by: Valentine Krasnobaeva <vkrasnobaeva@haproxy.com>
2025-05-05 16:17:24 +02:00
Willy Tarreau
3f2c8af313 MINOR: tools: make parse_line() provide hints about empty args
In order to help parse_line() callers report the position of empty
args to the user, let's decide that if no error is emitted, then
we'll stuff the errptr with the position of the first empty arg
without affecting the return value.

Co-authored-by: Valentine Krasnobaeva <vkrasnobaeva@haproxy.com>
2025-05-05 16:17:24 +02:00
Willy Tarreau
9d14f2c764 MEDIUM: config: warn about the consequences of empty arguments on a config line
For historical reasons, the config parser relies on the trailing '\0'
to detect the end of the line being parsed. When the lines started to be
tokenized into arguments, this principle has been preserved, and now all
the parsers rely on *args[arg]='\0' to detect the end of a line. But as
reported in issue #2944, while most of the time it breaks the parsing
like below:

     http-request deny if { path_dir '' }

it can also cause some elements to be silently ignored like below:

     acl bad_path path_sub '%2E' '' '%2F'

This may also subtly happen with environment variables that don't exist
or which are empty:

     acl bad_path path_sub '%2E' "$BAD_PATTERN" '%2F'

Fortunately, parse_line() returns the number of arguments found, so it's
easy from the callers to verify if any was empty. The goal of this commit
is not to perform sensitive changes, it's only to mention when parsing a
line that an empty argument was found and alert about its consequences
using a warning. Most of the time when this happens, the config does not
parse. But for examples as the ACLs above, there could be consequences
that are better detected early.

This patch depends on this previous fix:
   BUG/MINOR: tools: do not create an empty arg from trailing spaces

Co-authored-by: Valentine Krasnobaeva <vkrasnobaeva@haproxy.com>
2025-05-05 16:17:24 +02:00
Willy Tarreau
7e4a2f39ef BUG/MINOR: tools: do not create an empty arg from trailing spaces
Trailing spaces on the lines of the config file create an empty arg
which makes it complicated to detect really empty args. Let's first
address this. Note that it is not user-visible but prevents from
fixing user-visible issues. No backport is needed.

The initial issue was introduced with this fix that already tried to
address it:

    8a6767d266 ("BUG/MINOR: config: don't count trailing spaces as empty arg (v2)")

The current patch properly addresses leading and trailing spaces by
only counting arguments if non-lws chars were found on the line. LWS
do not cause a transition to a new arg anymore but they complete the
current one. The whole new code relies on a state machine to detect
when to create an arg (!in_arg->in_arg), and when to close the current
arg. A special care was taken for word expansion in the form of
"${ARGS[*]}" which still continue to emit individual arguments past
the first LWS. This example works fine:

    ARGS="100 check inter 1000"
    server name 192.168.1."${ARGS[*]}"

It properly results in 6 args:

    "server", "name", "192.168.1.100", "check", "inter", "1000"

This fix should not have any visible user impact and is a bit tricky,
so it's best not to backport it, at least for a while.

Co-authored-by: Valentine Krasnobaeva <vkrasnobaeva@haproxy.com>
2025-05-05 16:16:54 +02:00
William Lallemand
af5bbce664 BUG/MINOR: acme/cli: don't output error on success
Previous patch 7251c13c7 ("MINOR: acme: move the acme task init in a dedicated
function") mistakenly returned the wrong error code when "acme renew" parsing
was successful, and tried to emit an error message.

This patch fixes the issue by returning 0 when the acme task was correctly
scheduled to start.

No backport needed.
2025-05-02 21:21:09 +02:00
Aurelien DARRAGON
0e6f968ee3 BUG/MEDIUM: stktable: fix sc_*(<ctr>) BUG_ON() regression with ctx > 9
As reported in GH #2958, commit 6c9b315 caused a regression with sc_*
fetches and tracked counter id > 9.

As such, the below configuration would cause a BUG_ON() to be triggered:

  global
    log stdout format raw local0
    tune.stick-counters 11

  defaults
    log global
    mode http

  frontend www
    bind *:8080

    acl track_me bool(true)
    http-request set-var(txn.track_var) str("a")
    http-request track-sc10 var(txn.track_var) table rate_table if track_me
    http-request set-var(txn.track_var_rate) sc_gpc_rate(0,10,rate_table)
    http-request return status 200

  backend rate_table
      stick-table type string size 1k expire 5m store gpc_rate(1,1m)

While in 6c9b315 the src_fetch logic was removed from
smp_fetch_sc_stkctr(), num > 9 is indeed not expected anymore as
original num value. But what we didn't consider is that num is effectively
re-assigned for generic sc_* variant.

Thus the BUG_ON() is misplaced as it should only be evaluated for
non-generic fetches. It explains why it triggers with valid configurations

Thanks to GH user @tkjaer for his detailed report and bug analysis

No backport needed, this bug is specific to 3.2.
2025-05-02 16:57:45 +02:00
Willy Tarreau
758e0818c3 [RELEASE] Released version 3.2-dev14
Released version 3.2-dev14 with the following main changes :
    - MINOR: acme: retry label always do a request
    - MINOR: acme: does not leave task for next request
    - BUG/MINOR: acme: reinit the retries only at next request
    - MINOR: acme: change the default max retries to 5
    - MINOR: acme: allow a delay after a valid response
    - MINOR: acme: wait 5s before checking the challenges results
    - MINOR: acme: emit a log when starting
    - MINOR: acme: delay of 5s after the finalize
    - BUG/MEDIUM: quic: Let it be known if the tasklet has been released.
    - BUG/MAJOR: tasks: fix task accounting when killed
    - CLEANUP: tasks: use the local state, not t->state, to check for tasklets
    - DOC: acme: external account binding is not supported
    - MINOR: hlua: ignore "tune.lua.bool-sample-conversion" if set after "lua-load"
    - MEDIUM: peers: Give up if we fail to take locks in hot path
    - MEDIUM: stick-tables: defer adding updates to a tasklet
    - MEDIUM: stick-tables: Limit the number of old entries we remove
    - MEDIUM: stick-tables: Limit the number of entries we expire
    - MINOR: cfgparse-global: add explicit error messages in cfg_parse_global_env_opts
    - MINOR: ssl: add function to extract X509 notBefore date in time_t
    - BUILD: acme: need HAVE_ASN1_TIME_TO_TM
    - MINOR: acme: move the acme task init in a dedicated function
    - MEDIUM: acme: add a basic scheduler
    - MINOR: acme: emit a log when the scheduler can't start the task
2025-05-02 16:23:28 +02:00
William Lallemand
7ad501e6a1 MINOR: acme: emit a log when the scheduler can't start the task
Emit an error log when the renewal scheduler can't start the task.
2025-05-02 16:12:41 +02:00
William Lallemand
7fe59ebb88 MEDIUM: acme: add a basic scheduler
This patch implements a very basic scheduler for the ACME tasks.

The scheduler is a task which is started from the postparser function
when at least one acme section was configured.

The scheduler will loop over the certificates in the ckchs_tree, and for
each certificate will start an ACME task if the notAfter date is past
curtime + (notAfter - notBefore) / 12, or 7 days if notBefore is not
available.

Once the lookup over all certificates is terminated, the task will sleep
and will wakeup after 12 hours.
2025-05-02 16:01:32 +02:00
William Lallemand
7251c13c77 MINOR: acme: move the acme task init in a dedicated function
acme_start_task() is a dedicated function which starts an acme task
for a specified <store> certificate.

The initialization code was move from the "acme renew" command parser to
this function, in order to be called from a scheduler.
2025-05-02 16:01:32 +02:00
William Lallemand
878a3507df BUILD: acme: need HAVE_ASN1_TIME_TO_TM
Restrict the build of the ACME feature to libraries which provide
ASN1_TIME_to_tm() function.
2025-05-02 16:01:32 +02:00
William Lallemand
626de9538e MINOR: ssl: add function to extract X509 notBefore date in time_t
Add x509_get_notbefore_time_t() which returns the notBefore date in
time_t format.
2025-05-02 16:01:32 +02:00
Valentine Krasnobaeva
8a4b3216f9 MINOR: cfgparse-global: add explicit error messages in cfg_parse_global_env_opts
When env variable name or value are not provided for setenv/presetenv it's not
clear from the old error message shown at stderr, what exactly is missed. User
needs to search in it's configuration.

Let's add more explicit error messages about these inconsistencies.

No need to be backported.
2025-05-02 15:37:45 +02:00
Olivier Houchard
994cc58576 MEDIUM: stick-tables: Limit the number of entries we expire
In process_table_expire(), limit the number of entries we remove in one
call, and just reschedule the task if there's more to do. Removing
entries require to use the heavily contended update write lock, and we
don't want to hold it for too long.
This helps getting stick tables perform better under heavy load.
2025-05-02 15:27:55 +02:00
Olivier Houchard
d2d4c3eb65 MEDIUM: stick-tables: Limit the number of old entries we remove
Limit the number of old entries we remove in one call of
stktable_trash_oldest(), as we do so while holding the heavily contended
update write lock, so we'd rather not hold it for too long.
This helps getting stick tables perform better under heavy load.
2025-05-02 15:27:55 +02:00
Olivier Houchard
388539faa3 MEDIUM: stick-tables: defer adding updates to a tasklet
There is a lot of contention trying to add updates to the tree. So
instead of trying to add the updates to the tree right away, just add
them to a mt-list (with one mt-list per thread group, so that the
mt-list does not become the new point of contention that much), and
create a tasklet dedicated to adding updates to the tree, in batchs, to
avoid keeping the update lock for too long.
This helps getting stick tables perform better under heavy load.
2025-05-02 15:27:55 +02:00
Olivier Houchard
b3ad7b6371 MEDIUM: peers: Give up if we fail to take locks in hot path
In peer_send_msgs(), give up in order to retry later if we failed at
getting the update read lock.
Similarly, in __process_running_peer_sync(), give up and just reschedule
the task if we failed to get the peer lock. There is an heavy contention
on both those locks, so we could spend a lot of time trying to get them.
This helps getting peers perform better under heavy load.
2025-05-02 15:27:55 +02:00
Aurelien DARRAGON
7a8d1a3122 MINOR: hlua: ignore "tune.lua.bool-sample-conversion" if set after "lua-load"
tune.lua.bool-sample-conversion must be set before any lua-load or
lua-load-per-thread is used for it to be considered. Indeed, lua-load
directives are parsed on the fly and will cause some parts of the scripts
to be executed during init already (script body/init contexts).

As such, we cannot afford to have "tune.lua.bool-sample-conversion" set
after some Lua code was loaded, because it would mean that the setting
would be handled differently for Lua's code executed during or after
config parsing.

To avoid ambiguities, the documentation now states that the setting must
be set before any lua-load(-per-thread) directive, and if the setting
is met after some Lua was already loaded, the directive is ignored and
a warning informs about that.

It should fix GH #2957

It may be backported with 29b6d8af16 ("MINOR: hlua: rename
"tune.lua.preserve-smp-bool" to "tune.lua.bool-sample-conversion"")
2025-05-02 14:38:37 +02:00
William Lallemand
6051a6e485 DOC: acme: external account binding is not supported
Add a note on external account binding in the ACME section.
2025-05-02 12:04:07 +02:00
Willy Tarreau
1ed238101a CLEANUP: tasks: use the local state, not t->state, to check for tasklets
There's no point reading t->state to check for a tasklet after we've
atomically read the state into the local "state" variable. Not only it's
more expensive, it's also less clear whether that state is supposed to
be atomic or not. And in any case, tasks and tasklets have their type
forever and the one reflected in state is correct and stable.
2025-05-02 11:09:28 +02:00
Willy Tarreau
45e83e8e81 BUG/MAJOR: tasks: fix task accounting when killed
After recent commit b81c9390f ("MEDIUM: tasks: Mutualize the TASK_KILLED
code between tasks and tasklets"), the task accounting was no longer
correct for killed tasks due to the decrement of tasks in list that was
no longer done, resulting in infinite loops in process_runnable_tasks().
This just illustrates that this code remains complex and should be further
cleaned up. No backport is needed, as this was in 3.2.
2025-05-02 11:09:28 +02:00
Olivier Houchard
faa18c1ad8 BUG/MEDIUM: quic: Let it be known if the tasklet has been released.
quic_conn_release() may, or may not, free the tasklet associated with
the connection. So make it return 1 if it was, and 0 otherwise, so that
if it was called from the tasklet handler itself, the said handler can
act accordingly and return NULL if the tasklet was destroyed.
This should be backported if 9240cd4a2771245fae4d0d69ef025104b14bfc23
is backported.
2025-05-02 11:09:28 +02:00
William Lallemand
f63ceeded0 MINOR: acme: delay of 5s after the finalize
Let 5 seconds by default to the server after the finalize to generate
the certificate. Some servers would not send a Retry-After during
processing.
2025-05-02 10:34:48 +02:00
William Lallemand
2db4848fc8 MINOR: acme: emit a log when starting
Emit a administrative log when starting the ACME client for a
certificate.
2025-05-02 10:23:42 +02:00
William Lallemand
fbd740ef3e MINOR: acme: wait 5s before checking the challenges results
Wait 5 seconds before trying to check if the challenges are ready, so it
let time to server to execute the challenges.
2025-05-02 10:18:24 +02:00
William Lallemand
f7cae0e55b MINOR: acme: allow a delay after a valid response
Use the retryafter value to set a delay before doing the next request
when the previous response was valid.
2025-05-02 10:16:12 +02:00
William Lallemand
18d2371e0d MINOR: acme: change the default max retries to 5
Change the default max retries constant to 5 instead of 3.
Some servers can be be a bit long to execute the challenge.
2025-05-02 09:40:12 +02:00
William Lallemand
24fbd1f724 BUG/MINOR: acme: reinit the retries only at next request
The retries were reinitialized incorrectly, it must be reinit only
when we didn't retry. So any valid response would reinit the retries
number.
2025-05-02 09:34:45 +02:00
William Lallemand
6626011720 MINOR: acme: does not leave task for next request
The next request was always leaving the task befor initializing the
httpclient. This patch optimize it by jumping to the next step at the
end of the current one. This way, only the httpclient is doing a
task_wakeup() to handle the response. But transiting from response to
the next request does not leave the task.
2025-05-02 09:31:39 +02:00
William Lallemand
51f9415d5e MINOR: acme: retry label always do a request
Doing a retry always result in initializing a request again, set
ACME_HTTP_REQ directly in the label instead of doing it for each step.
2025-05-02 09:15:07 +02:00
Willy Tarreau
c589964bcc [RELEASE] Released version 3.2-dev13
Released version 3.2-dev13 with the following main changes :
    - MEDIUM: checks: Make sure we return the tasklet from srv_chk_io_cb
    - MEDIUM: listener: Make sure w ereturn the tasklet from accept_queue_process
    - MEDIUM: mux_fcgi: Make sure we return the tasklet from fcgi_deferred_shut
    - MEDIUM: quic: Make sure we return the tasklet from qcc_io_cb
    - MEDIUM: quic: Make sure we return NULL in quic_conn_app_io_cb if needed
    - MEDIUM: quic: Make sure we return the tasklet from quic_accept_run
    - BUG/MAJOR: tasklets: Make sure he tasklet can't run twice
    - BUG/MAJOR: listeners: transfer connection accounting when switching listeners
    - MINOR: ssl/cli: add a '-t' option to 'show ssl sni'
    - DOC: config: fix ACME paragraph rendering issue
    - DOC: config: clarify log-forward "host" option
    - MINOR: promex: expose ST_I_PX_RATE (current_session_rate)
    - BUILD: acme: use my_strndup() instead of strndup()
    - BUILD: leastconn: fix build warning when building without threads on old machines
    - MINOR: threads: prepare DEBUG_THREAD to receive more values
    - MINOR: threads: turn the full lock debugging to DEBUG_THREAD=2
    - MEDIUM: threads: keep history of taken locks with DEBUG_THREAD > 0
    - MINOR: threads/cli: display the lock history on "show threads"
    - MEDIUM: thread: set DEBUG_THREAD to 1 by default
    - BUG/MINOR: ssl/acme: free EVP_PKEY upon error
    - MINOR: acme: separate the code generating private keys
    - MINOR: acme: failure when no directory is specified
    - MEDIUM: acme: generate the account file when not found
    - MEDIUM: acme: use 'crt-base' to load the account key
    - MINOR: compiler: add more macros to detect macro definitions
    - MINOR: cli: split APPCTX_CLI_ST1_PROMPT into two distinct flags
    - MEDIUM: cli: make the prompt mode configurable between n/i/p
    - MEDIUM: mcli: make the prompt mode configurable between i/p
    - MEDIUM: mcli: replicate the current mode when enterin the worker process
    - DOC: configuration: acme account key are auto generated
    - CLEANUP: acme: remove old TODO for account key
    - DOC: configuration: add quic4 to the ssl-f-use example
    - BUG/MINOR: acme: does not try to unlock after a failed trylock
    - BUG/MINOR: mux-h2: fix the offset of the pattern for the ping frame
    - MINOR: tcp: add support for setting TCP_NOTSENT_LOWAT on both sides
    - BUG/MINOR: acme: creating an account should not end the task
    - MINOR: quic: rename min/max fields for congestion window algo
    - MINOR: quic: refactor BBR API
    - BUG/MINOR: quic: ensure cwnd limits are always enforced
    - MINOR: thread: define cshared type
    - MINOR: quic: account for global congestion window
    - MEDIUM: quic: limit global Tx memory
    - MEDIUM: acme: use a map to store tokens and thumbprints
    - BUG/MINOR: acme: remove references to virt@acme
    - MINOR: applet: add appctx_schedule() macro
    - BUG/MINOR: dns: add tempo between 2 connection attempts for dns servers
    - CLEANUP: dns: remove unused dns_stream_server struct member
    - BUG/MINOR: dns: prevent ds accumulation within dss
    - CLEANUP: proxy: mention that px->conn_retries isn't relevant in some cases
    - DOC: ring: refer to newer RFC5424
    - MINOR: tools: make my_strndup() take a size_t len instead of and int
    - MINOR: Add "sigalg" to "sigalg name" helper function
    - MINOR: ssl: Add traces to ssl init/close functions
    - MINOR: ssl: Add traces to recv/send functions
    - MINOR: ssl: Add traces to ssl_sock_io_cb function
    - MINOR: ssl: Add traces around SSL_do_handshake call
    - MINOR: ssl: Add traces to verify callback
    - MINOR: ssl: Add ocsp stapling callback traces
    - MINOR: ssl: Add traces to the switchctx callback
    - MINOR: ssl: Add traces about sigalg extension parsing in clientHello callback
    - MINOR: Add 'conn' param to ssl_sock_chose_sni_ctx
    - BUG/MEDIUM: mux-spop: Wait end of handshake to declare a spop connection ready
    - BUG/MEDIUM: mux-spop: Handle CLOSING state and wait for AGENT DISCONNECT frame
    - BUG/MINOR: mux-h1: Don't pretend connection was released for TCP>H1>H2 upgrade
    - BUG/MINOR: mux-h1: Fix trace message in h1_detroy() to not relay on connection
    - BUILD: ssl: Fix wolfssl build
    - BUG/MINOR: mux-spop: Use the right bitwise operator in spop_ctl()
    - MEDIUM: mux-quic: increase flow-control on each bufsize
    - MINOR: mux-quic: limit emitted MSD frames count per qcs
    - MINOR: add hlua_yield_asap() helper
    - MINOR: hlua_fcn: enforce yield after *_get_stats() methods
    - DOC: config: restore default values for resolvers hold directive
    - MINOR: ssl/cli: "acme ps" shows the acme tasks
    - MINOR: acme: acme_ctx_destroy() returns upon NULL
    - MINOR: acme: use acme_ctx_destroy() upon error
    - MEDIUM: tasks: Mutualize code between tasks and tasklets.
    - MEDIUM: tasks: More code factorization
    - MEDIUM: tasks: Remove TASK_IN_LIST and use TASK_QUEUED instead.
    - MINOR: tasks: Remove unused tasklet_remove_from_tasklet_list
    - MEDIUM: tasks: Mutualize the TASK_KILLED code between tasks and tasklets
    - BUG/MEDIUM: connections: Report connection closing in conn_create_mux()
    - BUILD/MEDIUM: quic: Make sure we build with recent changes
2025-04-30 18:25:28 +02:00
Olivier Houchard
81e4083efb BUILD/MEDIUM: quic: Make sure we build with recent changes
TASK_IN_LIST has been changed to TASK_QUEUED, but one was missed in
quic_conn.c, so fix that.
2025-04-30 18:00:56 +02:00
Olivier Houchard
b138eab302 BUG/MEDIUM: connections: Report connection closing in conn_create_mux()
Add an extra parametre to conn_create_mux(), "closed_connection".
If a pointer is provided, then let it know if the connection was closed.
Callers have no way to determine that otherwise, and we need to know
that, at least in ssl_sock_io_cb(), as if the connection was closed we
need to return NULL, as the tasklet was free'd, otherwise that can lead
to memory corruption and crashes.
This should be backported if 9240cd4a2771245fae4d0d69ef025104b14bfc23
is backported too.
2025-04-30 17:17:36 +02:00
Olivier Houchard
b81c9390f4 MEDIUM: tasks: Mutualize the TASK_KILLED code between tasks and tasklets
The code to handle a task/tasklet when it's been killed before it were
to run is mostly identical, so move it outside of task and tasklet
specific code, and inside the common code.

This commit is just cosmetic, and should have no impact.
2025-04-30 17:09:14 +02:00
Olivier Houchard
4abfade371 MINOR: tasks: Remove unused tasklet_remove_from_tasklet_list
Remove tasklet_remove_from_tasklet_list, as the function hasn't been
used for a long time, and there is little reason to keep it.
2025-04-30 17:09:06 +02:00
Olivier Houchard
2bab043c8c MEDIUM: tasks: Remove TASK_IN_LIST and use TASK_QUEUED instead.
TASK_QUEUED was used to mean "the task has been scheduled to run",
TASK_IN_LIST was used to mean "the tasklet has been scheduled to run",
remove TASK_IN_LIST and just use TASK_QUEUED for tasklets instead.

This commit is just cosmetic, and should not have any impact.
2025-04-30 17:08:57 +02:00
Olivier Houchard
35df7cbe34 MEDIUM: tasks: More code factorization
There is some code that should run no matter if the task was killed or
not, and was needlessly duplicated, so only use one instance.
This also fixes a small bug when a tasklet that got killed before it
could run would still count as a tasklet that ran, when it should not,
which just means that we'd run one less useful task before going back to
the poller.
This commit is mostly cosmetic, and should not have any impact.
2025-04-30 17:08:57 +02:00
Olivier Houchard
438c000e9f MEDIUM: tasks: Mutualize code between tasks and tasklets.
The code that checks if we're currently running, and waits if so, was
identical between tasks and tasklets, so move it in code common to tasks
and tasklets.
This commit is just cosmetic, and should not have any impact.
2025-04-30 17:08:57 +02:00
William Lallemand
6462f183ad MINOR: acme: use acme_ctx_destroy() upon error
Use acme_ctx_destroy() instead of a simple free() upon error in the
"acme renew" error handling.

It's better to use this function to be sure than everything has been
been freed.
2025-04-30 17:18:46 +02:00
William Lallemand
b8a5270334 MINOR: acme: acme_ctx_destroy() returns upon NULL
acme_ctx_destroy() returns when its argument is NULL.
2025-04-30 17:17:58 +02:00
William Lallemand
563ca94ab8 MINOR: ssl/cli: "acme ps" shows the acme tasks
Implement a way to display the running acme tasks over the CLI.

It currently only displays a "Running" status with the certificate name
and the acme section from the configuration.

The displayed running tasks are limited to the size of a buffer for now,
it will require a backref list later to be called multiple times to
resume the list.
2025-04-30 17:12:50 +02:00
Aurelien DARRAGON
4bceca83fc DOC: config: restore default values for resolvers hold directive
Default values for hold directive (resolver context) used to be documented
but this was lost when the keyword description was reworked in 24b319b
("Default value is 10s for "valid", 0s for "obsolete" and 30s for
others.")

Restoring the part that describes the default value.

It may be backported to all stable versions with 24b319b
2025-04-30 17:00:37 +02:00
Aurelien DARRAGON
7f418ac7d2 MINOR: hlua_fcn: enforce yield after *_get_stats() methods
{listener,proxy,server}_get_stats() methods are know to be expensive,
expecially if used under an iteration. Indeed, while automatic yield
is performed every X lua instructions (defaults to 10k), computing an
object's stats 10K times in a single cpu loop is not desirable and
could create contention.

In this patch we leverage hlua_yield_asap() at the end of *_get_stats()
methods in order to force the automatic yield to occur ASAP after the
method returns. Hopefully this should help in similar scenarios as the
one described in GH #2903
2025-04-30 17:00:31 +02:00
Aurelien DARRAGON
97363015a5 MINOR: add hlua_yield_asap() helper
When called, this function will try to enforce a yield (if available) as
soon as possible. Indeed, automatic yield is already enforced every X
Lua instructions. However, there may be some cases where we know after
running heavy operation that we should yield already to avoid taking too
much CPU at once.

This is what this function offers, instead of asking the user to manually
yield using "core.yield()" from Lua itself after using an expensive
Lua method offered by haproxy, we can directly enforce the yield without
the need to do it in the Lua script.
2025-04-30 17:00:27 +02:00
Amaury Denoyelle
df50d3e39f MINOR: mux-quic: limit emitted MSD frames count per qcs
The previous commit has implemented a new calcul method for
MAX_STREAM_DATA frame emission. Now, a frame may be emitted as soon as a
buffer was consumed by a QCS instance.

This will probably increase the number of MAX_STREAM_DATA frame
emission. It may even cause a series of frame emitted for the same
stream with increasing values under high load, which is completely
unnecessary.

To improve this, limit the number of MAX_STREAM_DATA frames built to one
per QCS instance. This is implemented by storing a reference to this
frame in QCS structure via a new member <tx.msd_frm>.

Note that to properly reset QCS msd_frm member, emission of flow-control
frames have been changed. Now, each frame is emitted individually. On
one side, it is better as it prevent to emit frames related to different
streams in a single datagram, which is not desirable in case of packet
loss. However, this can also increase sendto() syscall invocation.
2025-04-30 16:08:47 +02:00
Amaury Denoyelle
14a3fb679f MEDIUM: mux-quic: increase flow-control on each bufsize
Recently, QCS Rx allocation buffer method has been improved. It is now
possible to allocate multiple buffers per QCS instances, which was
necessary to improve HTTP/3 POST throughput.

However, a limitation remained related to the emission of
MAX_STREAM_DATA. These frames are only emitted once at least half of the
receive capacity has been consumed by its QCS instance. This may be too
restrictive when a client need to upload a large payload.

Improve this by adjusting MAX_STREAM_DATA allocation. If QCS capacity is
still limited to 1 or 2 buffers max, the old calcul is still used. This
is necessary when user has limited upload throughput via their
configuration. If QCS capacity is more than 2 buffers, a new frame is
emitted if at least a buffer was consumed.

This patch has reduced number of STREAM_DATA_BLOCKED frames received in
POST tests with some specific clients.
2025-04-30 16:08:47 +02:00
Christopher Faulet
2ccfebcebf BUG/MINOR: mux-spop: Use the right bitwise operator in spop_ctl()
Becaues of a typo, '||' was used instead of '|' to test the SPOP conneciton
flags and decide if the mux is ready or not. The regression was introduced
in the commit fd7ebf117 ("BUG/MEDIUM: mux-spop: Wait end of handshake to
declare a spop connection ready").

This patch must be backported to 3.1 with the commit above.
2025-04-30 16:01:36 +02:00
Remi Tricot-Le Breton
f191a830d8 BUILD: ssl: Fix wolfssl build
The newly added SSL traces require an extra 'conn' parameter to
ssl_sock_chose_sni_ctx which was added in the "regular" code but not in
the wolfssl specific one.
Wolfssl also has a different prototype for some getter functions
(SSL_get_servername for instance), which do not expect a const SSL while
openssl version does.
2025-04-30 15:50:10 +02:00
Christopher Faulet
7dc4e94830 BUG/MINOR: mux-h1: Fix trace message in h1_detroy() to not relay on connection
h1_destroy() may be called to release a H1C after a multiplexer upgrade. In
that case, the connection is no longer attached to the H1C. It must not be
used in the h1 trace message because the connection context is no longer a H1C.

Because of this bug, when a H1>H2 upgrade is performed, a crash may be
experienced if the H1 traces are enabled.

This patch must be backport to all stable versions.
2025-04-30 14:44:42 +02:00
Christopher Faulet
2dc334be61 BUG/MINOR: mux-h1: Don't pretend connection was released for TCP>H1>H2 upgrade
When an applicative upgrade of the H1 multiplexer is performed, we must not
pretend the connection was released.  Indeed, in that case, a H1 stream is
still their with a stream connector attached on it. It must be detached
first before releasing the H1 connection and the underlying connection. So
it is important to not pretend the connection was already released.

Concretely, in that case h1_process() must return 0 instead of -1. It is
minor error because, AFAIK, it is harmless. But it is not correct. So let's
fix it to avoid futur bugs.

To be clear, this happens when a TCP connection is upgraded to H1 connection
and a H2 preface is detected, leading to a second upgrade from H1 to H2.

This patch may be backport to all stable versions.
2025-04-30 14:44:42 +02:00
Christopher Faulet
53c3046898 BUG/MEDIUM: mux-spop: Handle CLOSING state and wait for AGENT DISCONNECT frame
In the SPOE specification, when an error occurred on the SPOP connection,
HAProxy must send a DISCONNECT frame and wait for the agent DISCONNECT frame
in return before trully closing the connection.

However, this part was not properly handled by the SPOP multiplexer. In this
case, the SPOP connection should be in the CLOSING state. But this state was
not used at all. Depending on when the error was encountered, the connection
could be closed immediately, without sending any DISCONNECT frame. It was
the case when an early error was detected during the AGENT-HELLO frame
parsing. Or it could be moved from ERROR to FRAME_H state, as if no error
were detected. This case was less dramatic than it seemed because some flags
were also set to prevent any problem. But it was not obvious.

So now, the SPOP connection is properly switch to CLOSING state when an
DISCONNECT is sent to the agent to be able to wait for its DISCONNECT in
reply. spop_process_demux() was updated to parse frames in that state and
some validity checks was added.

This patch must be backport to 3.1.
2025-04-30 14:44:42 +02:00
Christopher Faulet
fd7ebf117b BUG/MEDIUM: mux-spop: Wait end of handshake to declare a spop connection ready
A SPOP connection must not be considered as ready while the hello handshake
is not finished with success. In addition, no error or shutdown must have
been reported for the underlying connection. Otherwise a freshly openned
spop connexion may be reused while it is in fact dead, leading to a
connection retry.

This patch must be backported to 3.1.
2025-04-30 14:44:42 +02:00
Remi Tricot-Le Breton
047fb37b19 MINOR: Add 'conn' param to ssl_sock_chose_sni_ctx
This is only useful in the traces, the conn parameter won't be used
otherwise.
2025-04-30 11:11:26 +02:00
Remi Tricot-Le Breton
6519cec2ed MINOR: ssl: Add traces about sigalg extension parsing in clientHello callback
We had to parse the sigAlg extension by hand in order to properly select
the certificate used by the SSL frontends. These traces allow to dump
the allowed sigAlg list sent by the client in its clientHello.
2025-04-30 11:11:26 +02:00
Remi Tricot-Le Breton
105c1ca139 MINOR: ssl: Add traces to the switchctx callback
This callback allows to pick the used certificate on an SSL frontend.
The certificate selection is made according to the information sent by
the client in the clientHello. The traces that were added will allow to
better understand what certificate was chosen and why. It will also warn
us if the chosen certificate was the default one.
The actual certificate parsing happens in ssl_sock_chose_sni_ctx. It's
in this function that we actually get the filename of the certificate
used.
2025-04-30 11:11:26 +02:00
Remi Tricot-Le Breton
dbdd0630e1 MINOR: ssl: Add ocsp stapling callback traces
If OCSP stapling fails because of a missing or invalid OCSP response we
used to silently disable stapling for the given session. We can now know
a bit more what happened regarding OCSP stapling.
2025-04-30 11:11:26 +02:00
Remi Tricot-Le Breton
0fb05540b2 MINOR: ssl: Add traces to verify callback
Those traces allow to know which errors were met during certificate
chain validation as well as which ones were ignored.
2025-04-30 11:11:26 +02:00
Remi Tricot-Le Breton
4a8fa28e36 MINOR: ssl: Add traces around SSL_do_handshake call
Those traces dump information about the multiple SSL_do_handshake calls
(renegotiation and regular call). Some errors coud also be dumped in
case of rejected early data.
Depending on the chosen verbosity, some information about the current
handshake can be dumped as well (servername, tls version, chosen cipher
for instance).
In case of failed handshake, the error codes and messages will also be
dumped in the log to ease debugging.
2025-04-30 11:11:26 +02:00
Remi Tricot-Le Breton
9f146bdab3 MINOR: ssl: Add traces to ssl_sock_io_cb function
Add new SSL traces.
2025-04-30 11:11:26 +02:00
Remi Tricot-Le Breton
475bb8d843 MINOR: ssl: Add traces to recv/send functions
Those traces will allow to identify sessions on which early data is used
as well as some forcefully closed connections.
2025-04-30 11:11:26 +02:00
Remi Tricot-Le Breton
9bb8d6dcd1 MINOR: ssl: Add traces to ssl init/close functions
Add a dedicated trace for some unlikely allocation failures and async
errors. Those traces will ostly be used to identify the start and end of
a given SSL connection.
2025-04-30 11:11:26 +02:00
Remi Tricot-Le Breton
08e40f4589 MINOR: Add "sigalg" to "sigalg name" helper function
This function can be used to convert a TLSv1.3 sigAlg entry (2bytes)
from the signature_agorithms client hello extension into a string.

In order to ease debugging, some TLSv1.2 combinations can also be
dumped. In TLSv1.2 those signature algorithms pairs were built out of a
one byte signature identifier combined to a one byte hash identifier.
In TLSv1.3 those identifiers are two bytes blocs that must be treated as
such.
2025-04-30 11:11:26 +02:00
Willy Tarreau
566b384e4e MINOR: tools: make my_strndup() take a size_t len instead of and int
In relation to issue #2954, it appears that turning some size_t length
calculations to the int that uses my_strndup() upsets coverity a bit.
Instead of dealing with such warnings each time, better address it at
the root. An inspection of all call places show that the size passed
there is always positive so we can safely use an unsigned type, and
size_t will always suit it like for strndup() where it's available.
2025-04-30 05:17:43 +02:00
Lukas Tribus
5f9ce99c79 DOC: ring: refer to newer RFC5424
In the ring configuration example we refer to RFC3164 - the original BSD
syslog protocol without support for structured data (SDATA).

Let's refer to RFC5424 instead so SDATA is by default forwarded if
someone copy & pastes from the documentation:

https://discourse.haproxy.org/t/structured-data-lost-when-forwarding-logs-voa-syslog-forwarding-feature/11741/5

Should be backported to 2.6.
2025-04-29 21:39:01 +02:00
Aurelien DARRAGON
bd48e26a74 CLEANUP: proxy: mention that px->conn_retries isn't relevant in some cases
Since 91e785edc ("MINOR: stream: Rely on a per-stream max connection
retries value"), px->conn_retries may be ignored in the following cases:

 * proxy not part of a list which gets properly post-init (ie: main proxy
   list, log-forward list, sink list)
 * proxy lacking the CAP_FE capability

Documenting such cases where the px->conn_retries is set but effectively
ignored, so that we either remove ignored statements or fix them in
the future if they are really needed. In fact all cases affected here are
automomous applets that already handle the retries themselves so the fact
that 91e785edc made ->conn_retries ineffective should not be a big deal
anyway.
2025-04-29 21:21:19 +02:00
Aurelien DARRAGON
5288b39011 BUG/MINOR: dns: prevent ds accumulation within dss
when dns session callback (dns_session_release()) is called upon error
(ie: when some pending queries were not sent), we try our best to
re-create the applet in order to preserve the pending queries and give
them a chance to be retried. This is done at the end of
dns_session_release().

However, doing so exposes to an issue: if the error preventing queries
from being sent is still encountered over and over the dns session could
stay there indefinitely. Meanwhile, other dns sessions may be created on
the same dns_stream_server periodically. If previous failing dns sessions
don't terminate but we also keep creating new ones, we end up accumulating
failing sessions on a given dns_stream_server, which can eventually cause
ressource shortage.

This issue was found when trying to address ("BUG/MINOR: dns: add tempo
between 2 connection attempts for dns servers")

To fix it, we track the number of failed consecutive sessions for a given
dns server. When we reach the threshold (set to 100), we consider that the
link to the dns server is broken (at least temporarily) and we force
dns_session_new() to fail, so that we stop creating new sessions until one
of the existing one eventually succeeds.

A workaround for this fix consists in setting the "maxconn" parameter on
nameserver directive (under resolvers section) to a reasonnable value so
that no more than "maxconn" sessions may co-exist on the same server at
a given time.

This may be backported to all stable versions.
("CLEANUP: dns: remove unused dns_stream_server struct member") may be
backported to ease the backport.
2025-04-29 21:20:54 +02:00
Aurelien DARRAGON
14ebe95a10 CLEANUP: dns: remove unused dns_stream_server struct member
dns_stream_server "max_slots" is unused, let's get rid of it
2025-04-29 21:20:44 +02:00
Aurelien DARRAGON
27236f2218 BUG/MINOR: dns: add tempo between 2 connection attempts for dns servers
As reported by Lukas Tribus on the mailing list [1], trying to connect to
a nameserver with invalid network settings causes haproxy to retry a new
connection attempt immediately which eventually causes unexpected CPU usage
on the thread responsible for the applet (namely 100% on one CPU will be
observed).

This can be reproduced with the test config below:

 resolvers default
  nameserver ns1 tcp4@8.8.8.8:53 source 192.168.99.99
 listen listen
  mode http
  bind :8080
  server s1 www.google.com resolvers default init-addr none

To fix this the issue, we add a temporisation of one second between a new
connection attempt is retried. We do this in dns_session_create() when we
know that the applet was created in the release callback (when previous
query attempt was unsuccessful), which means initial connection is not
affected.

[1]: https://www.mail-archive.com/haproxy@formilux.org/msg45665.html

This should fix GH #2909 and may be backported to all stable versions.
This patch depends on ("MINOR: applet: add appctx_schedule() macro")
2025-04-29 21:20:11 +02:00
Aurelien DARRAGON
1ced5ef2fd MINOR: applet: add appctx_schedule() macro
Just like task_schedule() but for applets to wakeup an applet at a
specific time, leverages _task_schedule() internally
2025-04-29 21:19:37 +02:00
William Lallemand
c11ab983bf BUG/MINOR: acme: remove references to virt@acme
"virt@acme" was the default map used during development, now this must
be configured in the acme section or it won't try to use any map.

This patch removes the references to virt@acme in the comments and the
code.
2025-04-29 16:35:35 +02:00
William Lallemand
5555926fdd MEDIUM: acme: use a map to store tokens and thumbprints
The stateless mode which was documented previously in the ACME example
is not convenient for all use cases.

First, when HAProxy generates the account key itself, you wouldn't be
able to put the thumbprint in the configuration, so you will have to get
the thumbprint and then reload.
Second, in the case you are using multiple account key, there are
multiple thumbprint, and it's not easy to know which one you want to use
when responding to the challenger.

This patch allows to configure a map in the acme section, which will be
filled by the acme task with the token corresponding to the challenge,
as the key, and the thumbprint as the value. This way it's easy to reply
the right thumbprint.

Example:
    http-request return status 200 content-type text/plain lf-string "%[path,field(-1,/)].%[path,field(-1,/),map(virt@acme)]\n" if { path_beg '/.well-known/acme-challenge/' }
2025-04-29 16:15:55 +02:00
Amaury Denoyelle
0f9b3daf98 MEDIUM: quic: limit global Tx memory
Define a new settings tune.quic.frontend.max-tot-window. It contains a
size argument which can be used to set a limit on the sum of all QUIC
connections congestion window. This is applied both on
quic_cc_path_set() and quic_cc_path_inc().

Note that this limitation cannot reduce a congestion window more than
the minimal limit which is set to 2 datagrams.
2025-04-29 15:19:32 +02:00
Amaury Denoyelle
e841164a44 MINOR: quic: account for global congestion window
Use the newly defined cshared type to account for the sum of congestion
window of every QUIC connection. This value is stored in global counter
quic_mem_global defined in proto_quic module.
2025-04-29 15:19:32 +02:00
Amaury Denoyelle
3891456d20 MINOR: thread: define cshared type
Define a new type "struct cshared". This can be used as a tool to
manipulate a global counter with thread-safety ensured. Each thread
would declare its thread-local cshared type, which would point to a
global counter.

Each thread can then add/substract value to their owned thread-local
cshared instance via cshared_add(). If the difference exceed a
configured limit, either positively or negatively, the global counter is
updated and thread-local instance is reset to 0. Each thread can safely
read the global counter value using cshared_read().
2025-04-29 15:10:06 +02:00
Amaury Denoyelle
7bad88c35c BUG/MINOR: quic: ensure cwnd limits are always enforced
Congestion window is limit by a minimal and maximum values which can
never be exceeded. Min value is hardcoded to 2 datagrams as recommended
by the specification. Max value is specified via haproxy configuration.

These values must be respected each time the congestion window size is
adjusted. However, in some rare occasions, limit were not always
enforced. Fix this by implementing wrappers to set or increment the
congestion window. These functions ensure limits are always applied
after the operation.

Additionnally, wrappers also ensure that if window reached a new maximum
value, it is saved in <cwnd_last_max> field.

This should be backported up to 2.6, after a brief period of
observation.
2025-04-29 15:10:06 +02:00
Amaury Denoyelle
c01d455288 MINOR: quic: refactor BBR API
Write minor adjustments to QUIC BBR functions. The objective is to
centralize every modification of path cwnd field.

No functional change. This patch will be useful to simplify
implementation of global QUIC Tx memory usage limitation.
2025-04-29 15:10:06 +02:00
Amaury Denoyelle
2eb1b0cd96 MINOR: quic: rename min/max fields for congestion window algo
There was some possible confusion between fields related to congestion
window size min and max limit which cannot be exceeded, and the maximum
value previously reached by the window.

Fix this by adopting a new naming scheme. Enforced limit are now renamed
<limit_max>/<limit_min>, while the previously reached max value is
renamed <cwnd_last_max>.

This should be backported up to 3.1.
2025-04-29 15:10:06 +02:00
William Lallemand
62dfe1fc87 BUG/MINOR: acme: creating an account should not end the task
The account creation was mistakenly ending the task instead of being
wakeup for the NewOrder state, it was preventing the creation of the
certificate, however the account was correctly created.

To fix this, only the jump to the end label need to be remove, the
standard leaving codepath of the function will allow to be wakeup.

No backport needed.
2025-04-29 14:18:05 +02:00
Willy Tarreau
2cdb3cb91e MINOR: tcp: add support for setting TCP_NOTSENT_LOWAT on both sides
TCP_NOTSENT_LOWAT is very convenient as it indicates when to report
EAGAIN on the sending side. It takes a margin on top of the estimated
window, meaning that it's no longer needed to store too many data in
socket buffers. Instead there's just enough to fill the send window
and a little bit of margin to cover the scheduling time to restart
sending. Experiments on a 100ms network have shown a 10-fold reduction
in the memory used by socket buffers by just setting this value to
tune.bufsize, without noticing any performance degradation. Theoretically
the responsiveness on multiplexed protocols such as H2 should also be
improved.
2025-04-29 12:13:42 +02:00
Willy Tarreau
989f609b1a BUG/MINOR: mux-h2: fix the offset of the pattern for the ping frame
The ping frame's pattern must be written at offset 9 (frame header
length), not 8. This was added in 3.2 with commit 4dcfe098a6 ("MINOR:
mux-h2: prepare to support PING emission"), so no backport is needed.
2025-04-29 12:13:41 +02:00
William Lallemand
2f7f65e159 BUG/MINOR: acme: does not try to unlock after a failed trylock
Return after a failed trylock in acme_update_certificate() instead of
jumping to the error label which does an unlock.
2025-04-29 11:29:52 +02:00
William Lallemand
1cd0b35896 DOC: configuration: add quic4 to the ssl-f-use example
The ssl-f-use keyword is very useful in the case of multiple SSL bind
lines. Add a quic4 bind line in the example to show that.
2025-04-29 10:50:39 +02:00
William Lallemand
582614e1b2 CLEANUP: acme: remove old TODO for account key
Remove old TODO comments about the account key.
2025-04-29 09:59:32 +02:00
William Lallemand
59d83688e8 DOC: configuration: acme account key are auto generated
Explain that account key are auto generated when they do not exist.
2025-04-29 09:32:33 +02:00
Willy Tarreau
dc06495b71 MEDIUM: mcli: replicate the current mode when enterin the worker process
While humans can find it convenient to enter the worker process in prompt
mode, for external tools it will not be convenient to have to systematically
disable it. A better approach is to replicate the master socket's mode
there, since it has already been configured to suit the user: interactive,
prompt and timed modes are automatically passed to the worker process.
This makes the using the worker commands more natural from the master
process, without having to systematically adapt it for each new connection.
2025-04-28 20:21:06 +02:00
Willy Tarreau
c347cb73fa MEDIUM: mcli: make the prompt mode configurable between i/p
Support the same syntax in master mode as in worker mode in order to
configure the prompt. The only thing is that for now the master doesn't
have a non-interactive mode and it doesn't seem necessary to implement
it, so we only support the interactive and prompt modes. However the code
was written in a way that makes it easy to change this later if desired.
2025-04-28 20:21:06 +02:00
Willy Tarreau
e5c255c4e5 MEDIUM: cli: make the prompt mode configurable between n/i/p
Now the prompt mode can more finely be configured between non-interactive
(default), interactive without prompt, and interactive with prompt. This
will ease the usage from automated tools which are not necessarily
interested in having to consume '> ' after each command nor displaying
"+" on payload lines. This can also be convenient when coming from the
master CLI to keep the same output format.
2025-04-28 20:21:06 +02:00
Willy Tarreau
f25b4abc9b MINOR: cli: split APPCTX_CLI_ST1_PROMPT into two distinct flags
The CLI's "prompt" command toggles two distinct things:
  - displaying or hiding the prompt at the beginning of the line
  - single-command vs interactive mode

These are two independent concepts and the prompt mode doesn't
always cope well with tools that would like to upload data without
having to read the prompt on return. Also, the master command line
works in interactive mode by default with no prompt, which is not
consistent (and not convenient for tools). So let's start by splitting
the bit in two, and have a new APPCTX_CLI_ST1_INTER flag dedicated
to the interactive mode. For now the "prompt" command alone continues
to toggle the two at once.
2025-04-28 20:21:06 +02:00
Willy Tarreau
5ac280f2a7 MINOR: compiler: add more macros to detect macro definitions
We add __equals_0(NAME) which is only true if NAME is defined as zero,
and __def_as_empty(NAME) which is only true if NAME is defined as an
empty string.
2025-04-28 20:21:06 +02:00
William Lallemand
32b2b782e2 MEDIUM: acme: use 'crt-base' to load the account key
Prefix the filename with the 'crt-base' before loading the account key,
in order to work like every other keypair in haproxy.
2025-04-28 18:20:21 +02:00
William Lallemand
856b6042d3 MEDIUM: acme: generate the account file when not found
Generate the private key on the account file when the file does not
exists. This generate a private key of the type and parameters
configured in the acme section.
2025-04-28 18:20:21 +02:00
William Lallemand
b2dd6dd72b MINOR: acme: failure when no directory is specified
The "directory" parameter of the acme section is mandatory. This patch
exits with an alert when this parameter is not found.
2025-04-28 18:20:21 +02:00
William Lallemand
420de91d26 MINOR: acme: separate the code generating private keys
acme_EVP_PKEY_gen() generates private keys of specified <keytype>,
<curves> and <bits>. Only RSA and EC are supported for now.
2025-04-28 18:20:21 +02:00
William Lallemand
0897175d73 BUG/MINOR: ssl/acme: free EVP_PKEY upon error
Free the EPV_PKEY upon error when the X509_REQ generation failed.

No backport needed.
2025-04-28 18:20:21 +02:00
Willy Tarreau
12c7189bc8 MEDIUM: thread: set DEBUG_THREAD to 1 by default
Setting DEBUG_THREAD to 1 allows recording the lock history for each
thread. Tests have shown that (as predicted) the cost of updating a
single thread-local variable is not perceptible in the noise, especially
when compared to the cost of obtaining a lock. Since this can provide
useful value when debugging deadlocks, let's enable it by default when
threads are enabled.
2025-04-28 16:50:34 +02:00
Willy Tarreau
d9a659ed96 MINOR: threads/cli: display the lock history on "show threads"
This will display the lock labels and modes for each non-empty step
at the end of "show threads" when these are defined. This allows to
emit up to the last 8 locking operation for each thread on 64 bit
machines.
2025-04-28 16:50:34 +02:00
Willy Tarreau
b8a1c2380b MEDIUM: threads: keep history of taken locks with DEBUG_THREAD > 0
by only storing a word in each thread context, we can keep the history
of all taken/dropped locks by label. This is expected to be very cheap
and to permit to store up to 8 consecutive lock operations in 64 bits.
That should significantly help detect recursive locks as well as figure
what thread was likely to hinder another one waiting for a lock.

For now we only store the final state of the lock, we don't store the
attempt to get it. It's just a matter of space since we already need
4 ops (rd,sk,wr,un) which take 2 bits, leaving max 64 labels. We're
already around 45. We could also multiply by 5 and still keep 8 bits
total per lock, that would limit us to 51 locks max. It seems that
most of the time if we get a watchdog panic, anyway the victim thread
will be perfectly located so that we don't need a specific value for
this. Another benefit is that we perform a single memory write per
lock.
2025-04-28 16:50:34 +02:00
Willy Tarreau
23371b3e7c MINOR: threads: turn the full lock debugging to DEBUG_THREAD=2
At level 1 it now does nothing. This is reserved for some subsequent
patches which will implement lighter debugging.
2025-04-28 16:50:34 +02:00
Willy Tarreau
903a6b14ef MINOR: threads: prepare DEBUG_THREAD to receive more values
We now default the value to zero and make sure all tests properly take
care of values above zero. This is in preparation for supporting several
degrees of debugging.
2025-04-28 16:50:34 +02:00
Willy Tarreau
aa49965d4e BUILD: leastconn: fix build warning when building without threads on old machines
Machines lacking CAS8B/DWCAS and emit a warning in lb_fwlc.c without
threads due to declaration ordering. Let's just move the variable
declaration into the block that uses it as a last variable. No
backport is needed.
2025-04-28 16:50:34 +02:00
Willy Tarreau
589d916efa BUILD: acme: use my_strndup() instead of strndup()
Not all systems have strndup(), that's why we have our "my_strndup()",
so let's make use of it here. This fixes the build on Solaris 10. No
backport is needed.
2025-04-28 16:37:54 +02:00
Aurelien DARRAGON
dc95a3ed61 MINOR: promex: expose ST_I_PX_RATE (current_session_rate)
It has been requested to have the current_session_rate exposed at the
frontend level. For now only the per-process value was exposed
(ST_I_INF_SESS_RATE).

Thanks to the work done lately to merge promex and stat_cols_px[]
array, let's simply defined an .alt_name for the ST_I_PX_RATE metric in
order to have promex exposing it as current_session_rate for relevant
contexts.
2025-04-28 12:23:20 +02:00
Aurelien DARRAGON
e921362810 DOC: config: clarify log-forward "host" option
log-forward "host" option may be confusing because we often mention the
host field when talking about syslog RFC3164 or RFC5424 messages, but
neither rfc actually define "host" field. In fact, everywhere we used
"host field" we actually meant "hostname field" as documented in RFC5424.
This was a language abuse on our side.

In this patch we replace "host" with "hostname" where relevant in the
documentation to prevent confusion.

Thanks to Nick Ramirez for having reported the issue.
2025-04-28 12:23:16 +02:00
Aurelien DARRAGON
385b3f923f DOC: config: fix ACME paragraph rendering issue
Nick Ramirez reported that the ACME paragraph (3.13) caused a rendering
issue where simple text was rendered as a directive. This was caused
by the use of unescaped <name> which confuses dconv.

Let's escape <name> by putting quotes around it to prevent the rendering
issue.

No backport needed.
2025-04-28 12:23:12 +02:00
William Lallemand
83975f34e4 MINOR: ssl/cli: add a '-t' option to 'show ssl sni'
Add a -t option to 'show ssl sni', allowing to add an offset to the
current date so it would allow to check which certificates are expired
after a certain period of time.
2025-04-28 11:35:11 +02:00
Willy Tarreau
f1064c7382 BUG/MAJOR: listeners: transfer connection accounting when switching listeners
Since we made it possible for a bind_conf to listen to multiple thread
groups with shards in 2.8 with commit 9d360604bd ("MEDIUM: listener:
rework thread assignment to consider all groups"), the per-listener
connection count was not properly transferred to the target listener
with the connection when switching to another thread group. This results
in one listener possibly reaching high values and another one possibly
reaching negative values. Usually it's not visible, unless a maxconn is
set on the bind_conf, in which case comparisons will quickly put an end
to the willingness to accept new connections.

This problem only happens when thread groups are enabled, and it seems
very hard to trigger it normally, it only impacts sockets having a single
shard, hence currently the CLI (or any conf with "bind ... shards 1"),
where it can be reproduced with a config having a very low "maxconn" on
the stats socket directive (here, 4), and issuing a few tens of
socat <<< "show activity" in parallel, or sending HTTP connections to a
single-shared listener. Very quickly, haproxy stops accepting connections
and eats CPU in the poller which tries to get its connections accepted.

A BUG_ON(l->nbconn<0) after HA_ATOMIC_DEC() in listener_release() also
helps spotting them better.

Many thanks to Christian Ruppert who once again provided a very accurate
report in GH #2951 with the required data permitting this analysis.

This fix must be backported to 2.8.
2025-04-25 18:47:11 +02:00
Olivier Houchard
9240cd4a27 BUG/MAJOR: tasklets: Make sure he tasklet can't run twice
tasklets were originally designed to alway run on only one thread, so it
was not possible to have it run on 2 threads concurrently.
The API has been extended so that another thread may wake the tasklet,
the idea was still that we wanted to have it run on one thread only.
However, the way it's been done meant that unless a tasklet was bound to
a specific tid with tasklet_set_tid(), or we explicitely used
tasklet_wakeup_on() to specify the thread for the target to run on, it
would be scheduled to run on the current thread.
This is in fact a desirable feature. There is however a race condition
in which the tasklet would be scheduled on a thread, while it is running
on another. This could lead to the same tasklet to run on multiple
threads, which we do not want.
To fix this, just do what we already do for regular tasks, set the
"TASK_RUNNING" flag, and when it's time to execute the tasklet, wait
until that flag is gone.
Only one case has been found in the current code, where the tasklet
could run on different threads depending on who wakes it up, in the
leastconn load balancer, since commit
627280e15f03755b8f59f0191cd6d6bcad5afeb3.
It should not be a problem in practice, as the function called can be
called concurrently.
If a bug is eventually found in relation to this problem, and this patch
should be backported, the following patches should be backported too :
MEDIUM: quic: Make sure we return the tasklet from quic_accept_run
MEDIUM: quic: Make sure we return NULL in quic_conn_app_io_cb if needed
MEDIUM: quic: Make sure we return the tasklet from qcc_io_cb
MEDIUM: mux_fcgi: Make sure we return the tasklet from fcgi_deferred_shut
MEDIUM: listener: Make sure w ereturn the tasklet from accept_queue_process
MEDIUM: checks: Make sure we return the tasklet from srv_chk_io_cb
2025-04-25 16:14:26 +02:00
Olivier Houchard
09f5501bb9 MEDIUM: quic: Make sure we return the tasklet from quic_accept_run
In quic_accept_run, return the tasklet to tell the scheduler the tasklet
is still alive, it is not yet needed, but will be soon.
2025-04-25 16:14:26 +02:00
Olivier Houchard
5838786fa0 MEDIUM: quic: Make sure we return NULL in quic_conn_app_io_cb if needed
In quic_conn_app_io_cb, make sure we return NULL if the tasklet has been
destroyed, so that the scheduler knows. It is not yet needed, but will
be soon.
2025-04-25 16:14:26 +02:00
Olivier Houchard
15c5846db8 MEDIUM: quic: Make sure we return the tasklet from qcc_io_cb
In qcc_io_cb, return the tasklet to tell the scheduler the tasklet is
still alive, it is not yet needed, but will be soon.
2025-04-25 16:14:26 +02:00
Olivier Houchard
8f70f9c04b MEDIUM: mux_fcgi: Make sure we return the tasklet from fcgi_deferred_shut
In fcgi_deferred_shut, return the tasklet to tell the scheduler the
tasklet is still alive, it is not yet needed, but will be soon.
2025-04-25 16:14:26 +02:00
Olivier Houchard
7d190e7df6 MEDIUM: listener: Make sure w ereturn the tasklet from accept_queue_process
In accept_queue_process, return the tasklet to tell the scheduler the
tasklet is still alive, it is not yet needed, but will be soon.
2025-04-25 16:14:26 +02:00
Olivier Houchard
81dc3e67cf MEDIUM: checks: Make sure we return the tasklet from srv_chk_io_cb
In srv_chk_io_cb, return the tasklet to tell the scheduler the tasklet
is still alive, it is not yet needed, but will be soon.
2025-04-25 16:14:26 +02:00
Willy Tarreau
beb23069c6 [RELEASE] Released version 3.2-dev12
Released version 3.2-dev12 with the following main changes :
    - BUG/MINOR: quic: do not crash on CRYPTO ncbuf alloc failure
    - BUG/MINOR: proxy: always detach a proxy from the names tree on free()
    - CLEANUP: proxy: detach the name node in proxy_free_common() instead
    - CLEANUP: Slightly reorder some proxy option flags to free slots
    - MINOR: proxy: Add options to drop HTTP trailers during message forwarding
    - MINOR: h1-htx: Skip C-L and T-E headers for 1xx and 204 messages during parsing
    - MINOR: mux-h1: Keep custom "Content-Length: 0" header in 1xx and 204 messages
    - MINOR: hlua/h1: Use http_parse_cont_len_header() to parse content-length value
    - CLEANUP: h1: Remove now useless h1_parse_cont_len_header() function
    - BUG/MEDIUM: mux-spop: Respect the negociated max-frame-size value to send frames
    - MINOR: http-act: Add 'pause' action to temporarily suspend the message analysis
    - MINOR: acme/cli: add the 'acme renew' command to the help message
    - MINOR: httpclient: add an "https" log-format
    - MEDIUM: acme: use a customized proxy
    - MEDIUM: acme: rename "uri" into "directory"
    - MEDIUM: acme: rename "account" into "account-key"
    - MINOR: stick-table: use a separate lock label for updates
    - MINOR: h3: simplify h3_rcv_buf return path
    - BUG/MINOR: mux-quic: fix possible infinite loop during decoding
    - BUG/MINOR: mux-quic: do not decode if conn in error
    - BUG/MINOR: cli: Issue an error when too many args are passed for a command
    - MINOR: cli: Use a full prompt command for bidir connections with workers
    - MAJOR: cli: Refacor parsing and execution of pipelined commands
    - MINOR: cli: Rename some CLI applet states to reflect recent refactoring
    - CLEANUP: applet: Update st0/st1 comment in appctx structure
    - BUG/MINOR: hlua: Fix I/O handler of lua CLI commands to not rely on the SC
    - BUG/MINOR: ring: Fix I/O handler of "show event" command to not rely on the SC
    - MINOR: cli/applet: Move appctx fields only used by the CLI in a private context
    - MINOR: cache: Add a pointer on the cache in the cache applet context
    - MINOR: hlua: Use the applet name in error messages for lua services
    - MINOR: applet: Save the "use-service" rule in the stream to init a service applet
    - CLEANUP: applet: Remove unsued rule pointer in appctx structure
    - BUG/MINOR: master/cli: properly trim the '@@' process name in error messages
    - MEDIUM: resolvers: add global "dns-accept-family" directive
    - MINOR: resolvers: add command-line argument -4 to force IPv4-only DNS
    - MINOR: sock-inet: detect apparent IPv6 connectivity
    - MINOR: resolvers: add "dns-accept-family auto" to rely on detected IPv6
    - MEDIUM: acme: use Retry-After value for retries
    - MEDIUM: acme: reset the remaining retries
    - MEDIUM: acme: better error/retry management of the challenge checks
    - BUG/MEDIUM: cli: Handle applet shutdown when waiting for a command line
    - Revert "BUG/MINOR: master/cli: properly trim the '@@' process name in error messages"
    - BUG/MINOR: master/cli: only parse the '@@' prefix on complete lines
    - MINOR: resolvers: use the runtime IPv6 status instead of boot time one
2025-04-25 10:19:03 +02:00
Willy Tarreau
40aceb7414 MINOR: resolvers: use the runtime IPv6 status instead of boot time one
On systems where the network is not reachable at boot time (certain HA
systems for example, or dynamically addressed test machines), we'll want
to be able to periodically revalidate the IPv6 reachability status. The
current code makes it complicated because it sets the config bits once
for all at boot time. This commit changes this so that the config bits
are not changed, but instead we rely on a static inline function that
relies on sock_inet6_seems_reachable for every test (really cheap). This
also removes the now unneeded resolvers late init code.

This variable for now is still set at boot time but this will ease the
transition later, as the resolvers code is now ready for this.
2025-04-25 09:32:05 +02:00
Willy Tarreau
7a79f54c98 BUG/MINOR: master/cli: only parse the '@@' prefix on complete lines
The new adhoc parser for the '@@' prefix forgot to require the presence
of the LF character marking the end of the line. This is the reason why
entering incomplete commands would display garbage, because the line was
expected to have its LF character replaced with a zero.

The problem is well illustrated by using socat in raw mode:

   socat /tmp/master.sock STDIO,raw,echo=0

then entering "@@1 show info" one character at a time would error just
after the second "@". The command must take care to report an incomplete
line and wait for more data in such a case.
2025-04-25 09:05:00 +02:00
Willy Tarreau
931d932b3e Revert "BUG/MINOR: master/cli: properly trim the '@@' process name in error messages"
This reverts commit 0e94339eaf1c8423132debb6b1b485d8bb1bb7da.

This patch was in fact fixing the symptom, not the cause. The root cause
of the problem is that the parser was processing an incomplete line when
looking for '@@'. When the LF is present, this problem does not exist
as it's properly replaced with a zero. This can be verified using socat
in raw mode:

  socat /tmp/master.sock STDIO,raw,echo=0

Then entering "@@1 show info" one character at a time will immediately
fail on "@@" without going further. A subsequent patch will fix this.
No backport is needed.
2025-04-25 09:05:00 +02:00
Christopher Faulet
101cc4f334 BUG/MEDIUM: cli: Handle applet shutdown when waiting for a command line
When the CLI applet was refactord in the commit 20ec1de21 ("MAJOR: cli:
Refacor parsing and execution of pipelined commands"), a regression was
introduced. The applet shutdown was not longer handled when the applet was
waiting for the next command line. It is especially visible when a client
timeout occurred because the client connexion is no longer closed.

To fix the issue, the test on the SE_FL_SHW flag was reintroduced in
CLI_ST_PARSE_CMDLINE state, but only is there is no pending input data.

It is a 3.2-specific issue. No backport needed.
2025-04-25 08:47:05 +02:00
William Lallemand
27b732a661 MEDIUM: acme: better error/retry management of the challenge checks
When the ACME task is checking for the status of the challenge, it would
only succeed or retry upon failure.

However that's not the best way to do it, ACME objects contain an
"status" field which could have a final status or a in progress status,
so we need to be able to retry.

This patch adds an acme_ret enum which contains OK, RETRY and FAIL.

In the case of the CHKCHALLENGE, the ACME could return a "pending" or a
"processing" status, which basically need to be rechecked later with the
RETRY. However a "invalid" or "valid" status is final and will return
either a FAIL or a OK.

So instead of retrying in any case, the "invalid" status will ends the
task with an error.
2025-04-24 20:14:47 +02:00
William Lallemand
0909832e74 MEDIUM: acme: reset the remaining retries
When a request succeed, reset the remaining retries to the default
ACME_RETRY value (3 by default).
2025-04-24 20:14:47 +02:00
William Lallemand
bb768b3e26 MEDIUM: acme: use Retry-After value for retries
Parse the Retry-After header in response and store it in order to use
the value as the next delay for the next retry, fallback to 3s if the
value couldn't be parse or does not exist.
2025-04-24 20:14:47 +02:00
Willy Tarreau
69b051d1dc MINOR: resolvers: add "dns-accept-family auto" to rely on detected IPv6
Instead of always having to force IPv4 or IPv6, let's now also offer
"auto" which will only enable IPv6 if the system has a default gateway
for it. This means that properly configured dual-stack systems will
default to "ipv4,ipv6" while those lacking a gateway will only use
"ipv4". Note that no real connectivity test is performed, so firewalled
systems may still get it wrong and might prefer to rely on a manual
"ipv4" assignment.
2025-04-24 17:52:28 +02:00
Willy Tarreau
5d41d476f3 MINOR: sock-inet: detect apparent IPv6 connectivity
In order to ease dual-stack deployments, we could at least try to
check if ipv6 seems to be reachable. For this we're adding a test
based on a UDP connect (no traffic) on port 53 to the base of
public addresses (2001::) and see if the connect() is permitted,
indicating that the routing table knows how to reach it, or fails.
Based on this result we're setting a global variable that other
subsystems might use to preset their defaults.
2025-04-24 17:52:28 +02:00
Willy Tarreau
2c46c2c042 MINOR: resolvers: add command-line argument -4 to force IPv4-only DNS
In order to ease troubleshooting and testing, the new "-4" command line
argument enforces queries and processing of "A" DNS records only, i.e.
those representing IPv4 addresses. This can be useful when a host lack
end-to-end dual-stack connectivity. This overrides the global
"dns-accept-family" directive and is equivalent to value "ipv4".
2025-04-24 17:52:28 +02:00
Willy Tarreau
940fa19ad8 MEDIUM: resolvers: add global "dns-accept-family" directive
By default, DNS resolvers accept both IPv4 and IPv6 addresses. This can be
influenced by the "resolve-prefer" keywords on server lines as well as the
family argument to the "do-resolve" action, but that is only a preference,
which does not block the other family from being used when it's alone. In
some environments where dual-stack is not usable, stumbling on an unreachable
IPv6-only DNS record can cause significant trouble as it will replace a
previous IPv4 one which would possibly have continued to work till next
request. The "dns-accept-family" global option permits to enforce usage of
only one (or both) address families. The argument is a comma-delimited list
of the following words:
  - "ipv4": query and accept IPv4 addresses ("A" records)
  - "ipv6": query and accept IPv6 addresses ("AAAA" records)

When a single family is used, no request will be sent to resolvers for the
other family, and any response for the othe family will be ignored. The
default value is "ipv4,ipv6", which effectively enables both families.
2025-04-24 17:52:28 +02:00
Willy Tarreau
0e94339eaf BUG/MINOR: master/cli: properly trim the '@@' process name in error messages
When '@@' alone is sent on the master CLI (no trailing LF), we get an
error that displays anything past these two characters in the buffer
since there's no room for a \0. Let's make sure to limit the length of
the process name in this case. No backport is needed since this was added
with 00c967fac4 ("MINOR: master/cli: support bidirectional communications
with workers").
2025-04-24 17:52:28 +02:00
Christopher Faulet
29632bcabf CLEANUP: applet: Remove unsued rule pointer in appctx structure
Thanks to previous commits, the "rule" field in the appctx structure is no
longer used. So we can safely remove it.
2025-04-24 16:22:31 +02:00
Christopher Faulet
568ed6484a MINOR: applet: Save the "use-service" rule in the stream to init a service applet
When a service is initialized, the "use-service" rule that was executed is
now saved in the stream, using "current_rule" field, instead of saving it
into the applet context. It is safe to do so becaues this field is unused at
this stage. To avoid any issue, it is reset after the service
initialization. Doing so, it is no longer necessary to save it in the applet
context. It was the last usage of the rule pointer in the applet context.

The init functions for TCP and HTTP lua services were updated accordingly.
2025-04-24 16:22:24 +02:00
Christopher Faulet
6f59986e7c MINOR: hlua: Use the applet name in error messages for lua services
The lua function name was used in error messages of HTTP/TCP lua services
while the applet name can be used. Concretely, this will not change
anything, because when a lua service is regiestered, the lua function name
is used to name the applet. But it is easier, cleaner and more logicial
because it is really the applet name that should be displayed in these error
messages.
2025-04-24 15:59:33 +02:00
Christopher Faulet
e05074f632 MINOR: cache: Add a pointer on the cache in the cache applet context
Thanks to this change, when a response is delivered from the cache, it is no
longer necessary to get the cache filter configuration from the http
"use-cache" rule saved in the appctx to get the currently used cache. It was
a bit complex to get an info that can be directly and naturally stored in
the cache applet context.
2025-04-24 15:48:59 +02:00
Christopher Faulet
b734d7c156 MINOR: cli/applet: Move appctx fields only used by the CLI in a private context
There are several fields in the appctx structure only used by the CLI. To
make things cleaner, all these fields are now placed in a dedicated context
inside the appctx structure. The final goal is to move it in the service
context and add an API for cli commands to get a command coontext inside the
cli context.
2025-04-24 15:09:37 +02:00
Christopher Faulet
03dc54d802 BUG/MINOR: ring: Fix I/O handler of "show event" command to not rely on the SC
Thanks to the CLI refactoring ("MAJOR: cli: Refacor parsing and execution of
pipelined commands"), it is possible to fix "show event" I/O handle function
to no longer use the SC.

When the applet API was refactored to no longer manipulate the channels or
the stream-connectors, this part was missed. However, without the patch
above, it could not be fixed. It is now possible so let's do it.

This patch must not be backported becaues it depends on refactoring of the
CLI applet.
2025-04-24 15:09:37 +02:00
Christopher Faulet
e406fe16ea BUG/MINOR: hlua: Fix I/O handler of lua CLI commands to not rely on the SC
Thanks to the CLI refactoring ("MAJOR: cli: Refacor parsing and execution of
pipelined commands"), it is possible to fix the I/O handler function used by
lua CLI commands to no longer use the SC.

When the applet API was refactored to no longer manipulate the channels or
the stream-connectors, this part was missed. However, without the patch
above, it could not be fixed. It is now possible so let's do it.

This patch must not be backported becaues it depends on refactoring of the
CLI applet.
2025-04-24 15:09:37 +02:00
Christopher Faulet
742dc01537 CLEANUP: applet: Update st0/st1 comment in appctx structure
Today, these states are used by almost all applets. So update the comments
of these fields.
2025-04-24 15:09:37 +02:00
Christopher Faulet
44ace9a1b7 MINOR: cli: Rename some CLI applet states to reflect recent refactoring
CLI_ST_GETREQ state was renamed into CLI_ST_PARSE_CMDLINE and CLI_ST_PARSEREQ
into CLI_ST_PROCESS_CMDLINE to reflect the real action performed in these
states.
2025-04-24 15:09:37 +02:00
Christopher Faulet
20ec1de214 MAJOR: cli: Refacor parsing and execution of pipelined commands
Before this patch, when pipelined commands were received, each command was
parsed and then excuted before moving to the next command. Pending commands
were not copied in the input buffer of the applet. The major issue with this
way to handle commands is the impossibility to consume inputs from commands
with an I/O handler, like "show events" for instance. It was working thanks
to a "bug" if such commands were the last one on the command line. But it
was impossible to use them followed by another command. And this prevents us
to implement any streaming support for CLI commands.

So we decided to refactor the command line parsing to have something similar
to a basic shell. Now an entire line is parsed, including the payload,
before starting commands execution. The command line is copied in a
dedicated buffer. "appctx->chunk" buffer is used for this purpose. It was an
unsed field, so it is safe to use it here. Once the command line copied, the
commands found on this line are executed. Because the applet input buffer
was flushed, any input can be safely consumed by the CLI applet and is
available for the command I/O handler. Thanks to this change, "show event
-w" command can be followed by a command. And in theory, it should be
possible to implement commands supporting input data streaming. For
instance, the Tetris like lua applet can be used on the CLI now.

Note that the payload, if any, is part of the command line and must be fully
received before starting the commands processing. It means there is still
the limitation to a buffer, but not only for the payload but for the whole
command line. The payload is still necessarily at the end of the command
line and is passed as argument to the last command. Internally, the
"appctx->cli_payload" field was introduced to point on the payload in the
command line buffer.

This patch is quite huge but it cannot easily be splitted. It should not
introduced significant changes.
2025-04-24 15:09:37 +02:00
Christopher Faulet
69a9ec5bef MINOR: cli: Use a full prompt command for bidir connections with workers
When a bidirection connection with no command is establisehd with a worker
(so "@@<pid>" alone), a "prompt" command is automatically added to display
the worker's prompt and enter in interactive mode in the worker context.
However, till now, an unfinished command line is sent, with a semicolon
instead of a newline at the end. It is not exactly a bug because this
works. But it is not really expected and could be a problem for future
changes.

So now, a full command line is sent: the "prompt" command finished by a
newline character.
2025-04-24 15:09:37 +02:00
Christopher Faulet
d3f9289447 BUG/MINOR: cli: Issue an error when too many args are passed for a command
When a command is parsed to split it in an array of arguments, by default,
at most 64 arguments are supported. But no warning was emitted when there
were too many arguments. Instead, the arguments above the limit were
silently ignored. It could be an issue for some commands, like "add server",
because there was no way to know some arguments were ignored.

Now an error is issued when too many arguments are passed and the command is
not executed.

This patch should be backported to all stable versions.
2025-04-24 14:58:24 +02:00
Amaury Denoyelle
6c5030f703 BUG/MINOR: mux-quic: do not decode if conn in error
Add an early return to qcc_decode_qcs() if QCC instance is flagged on
error and connection is scheduled for immediate closure.

The main objective is to ensure to not trigger BUG_ON() from
qcc_set_error() : if a stream decoding has set the connection error, do
not try to process decoding on other streams as they may also encounter
an error. Thus, the connection is closed asap with the first encountered
error case.

This should be backported up to 2.6, after a period of observation.
2025-04-24 14:15:02 +02:00
Amaury Denoyelle
fbedb8746f BUG/MINOR: mux-quic: fix possible infinite loop during decoding
With the support of multiple Rx buffers per QCS instance, stream
decoding in qcc_io_recv() has been reworked for the next haproxy
release. An issue appears in a double while loop : a break statement is
used in the inner loop, which is not sufficient as it should instead
exit from the outer one.

Fix this by replacing break with a goto statement.

No need to backport this.
2025-04-24 14:15:02 +02:00
Amaury Denoyelle
3dcda87e58 MINOR: h3: simplify h3_rcv_buf return path
Remove return statement in h3_rcv_buf() in case of stream/connection
error. Instead, reuse already existing label err. This simplifies the
code path. It also fixes the missing leave trace for these cases.
2025-04-24 14:15:02 +02:00
Willy Tarreau
1af592c511 MINOR: stick-table: use a separate lock label for updates
Too many locks were sharing STK_TABLE_LOCK making it hard to analyze.
Let's split the already heavily used update lock.
2025-04-24 14:02:22 +02:00
William Lallemand
f192e446d6 MEDIUM: acme: rename "account" into "account-key"
Rename the "account" option of the acme section into "account-key".
2025-04-24 11:10:46 +02:00
William Lallemand
af73f98a3e MEDIUM: acme: rename "uri" into "directory"
Rename the "uri" option of the acme section into "directory".
2025-04-24 10:52:46 +02:00
William Lallemand
4e14889587 MEDIUM: acme: use a customized proxy
Use a customized proxy for the ACME client.

The proxy is initialized at the first acme section parsed.

The proxy uses the httpsclient log format as ACME CA use HTTPS.
2025-04-23 15:37:57 +02:00
William Lallemand
d700a242b4 MINOR: httpclient: add an "https" log-format
Add an experimental "https" log-format for the httpclient, it is not
used by the httpclient by default, but could be define in a customized
proxy.

The string is basically a httpslog, with some of the fields replaced by
their backend equivalent or - when not available:

"%ci:%cp [%tr] %ft -/- %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r %[bc_err]/%[ssl_bc_err,hex]/-/-/%[ssl_bc_is_resumed] -/-/-"
2025-04-23 15:32:46 +02:00
William Lallemand
d19a62dc65 MINOR: acme/cli: add the 'acme renew' command to the help message
Add the 'acme renew' command to the 'help' command of the CLI.
2025-04-23 13:59:27 +02:00
Christopher Faulet
1709cfd31d MINOR: http-act: Add 'pause' action to temporarily suspend the message analysis
The 'pause' HTTP action can now be used to suspend for a moment the message
analysis. A timeout, expressed in milliseconds using a time-format
parameter, or an expression can be used. If an expression is used, errors
and invalid values are ignored.

Internally, the action will set the analysis expiration date on the
corresponding channel to the configured value and it will yield while it is
not expired.

The 'pause' action is available for 'http-request' and 'http-response'
rules.
2025-04-22 16:14:47 +02:00
Christopher Faulet
ce8c2d359b BUG/MEDIUM: mux-spop: Respect the negociated max-frame-size value to send frames
When a SPOP connection is opened, the maximum size for frames is negociated.
This negociated size is properly used when a frame is received and if a too
big frame is detected, an error is triggered. However, the same was not
performed on the sending path. No check was performed on frames sent to the
agent. So it was possible to send frames bigger than the maximum size
supported by the the SPOE agent.

Now, the size of NOTIFY and DISCONNECT frames is checked before sending them
to the agent.

Thanks to Miroslav to have reported the issue.

This patch must be backported to 3.1.
2025-04-22 16:14:47 +02:00
Christopher Faulet
a56feffc6f CLEANUP: h1: Remove now useless h1_parse_cont_len_header() function
Since the commit "MINOR: hlua/h1: Use http_parse_cont_len_header() to parse
content-length value", this function is no longer used. So it can be safely
removed.
2025-04-22 16:14:47 +02:00
Christopher Faulet
9e05c14a41 MINOR: hlua/h1: Use http_parse_cont_len_header() to parse content-length value
Till now, h1_parse_cont_len_header() was used during the H1 message parsing and
by the lua HTTP applets to parse the content-length header value. But a more
generic function was added some years ago doing exactly the same operations. So
let's use it instead.
2025-04-22 16:14:47 +02:00
Christopher Faulet
a6b32922fc MINOR: mux-h1: Keep custom "Content-Length: 0" header in 1xx and 204 messages
Thanks to the commit "MINOR: mux-h1: Don't remove custom "Content-Length: 0"
header in 1xx and 204 messages", we are now sure that 1xx and 204 responses
were sanitized during the parsing. So, if one of these headers are found in
such responses when sent to the client, it means it was added by hand, via a
"set-header" action for instance. In this context, we are able to make an
exception for the "Content-Length: 0" header, and only this one with this
value, to not break leagacy applications.

So now, a user can force the "Content-Length: 0" header to appear in 1xx and
204 responses by adding the right action in hist configuration.
"Transfer-Encoding" headers are still dropped as "Content-Length" headers
with another value than 0. Note, that in practice, only 101 and 204 are
concerned because other 1xx message are not subject to HTTP analysis.

This patch should fix the issue #2888. There is no reason to backport
it. But if we do so, the patch above must be backported too.
2025-04-22 16:14:47 +02:00
Christopher Faulet
1db99b09d0 MINOR: h1-htx: Skip C-L and T-E headers for 1xx and 204 messages during parsing
According to the RFC9110 and RFC9112, a server must not add 'Content-Length'
or 'Transfer-Encoding' headers into 1xx and 204 responses. So till now,
these headers were dropped from the response when it is sent to the client.

However, it seems more logical to remove it during the message parsing. In
addition to sanitize messages as early as possible, this will allow us to
apply some exception in some cases (This will be the subject of another
patch).

In this patch, 'Content-Length' and 'Transfer-Encoding' headers are removed
from 1xx and 204 responses during the parsing but the same is still
performed during the formatting stage.
2025-04-22 16:14:47 +02:00
Christopher Faulet
5200203677 MINOR: proxy: Add options to drop HTTP trailers during message forwarding
In RFC9110, it is stated that trailers could be merged with the
headers. While it should be performed with a speicial care, it may be a
problem for some applications. To avoid any trouble with such applications,
two new options were added to drop trailers during the message forwarding.

On the backend, "http-drop-request-trailers" option can be enabled to drop
trailers from the requests before sending them to the server. And on the
frontend, "http-drop-response-trailers" option can be enabled to drop
trailers from the responses before sending them to the client. The options
can be defined in defaults sections and disabled with "no" keyword.

This patch should fix the issue #2930.
2025-04-22 16:14:46 +02:00
Christopher Faulet
044ef9b3d6 CLEANUP: Slightly reorder some proxy option flags to free slots
PR_O_TCPCHK_SSL and PR_O_CONTSTATS was shifted to free a slot. The idea is
to have 2 contiguous slots to be able to insert two new options.
2025-04-22 16:14:46 +02:00
Willy Tarreau
5763a891a9 CLEANUP: proxy: detach the name node in proxy_free_common() instead
This changes commit d2a9149f0 ("BUG/MINOR: proxy: always detach a proxy
from the names tree on free()") to be cleaner. Aurlien spotted that
the free(p->id) was indeed already done in proxy_free_common(), which is
called before we delete the node. That's still a bit ugly and it only
works because ebpt_delete() does not dereference the key during the
operation. Better play safe and delete the entry before freeing it,
that's more future-proof.
2025-04-19 10:21:19 +02:00
Willy Tarreau
d2a9149f09 BUG/MINOR: proxy: always detach a proxy from the names tree on free()
Stephen Farrell reported in issue #2942 that recent haproxy versions
crash if there's no resolv.conf. A quick bisect with his reproducer
showed that it started with commit 4194f75 ("MEDIUM: tree-wide: avoid
manually initializing proxies") which reorders the proxies initialization
sequence a bit. The crash shows a corrupted tree, typically indicating a
use-after-free. With the help of ASAN it was possible to find that a
resolver proxy had been destroyed and freed before the name insertion
that causes the crash, very likely caused by the absence of the needed
resolv.conf:

    #0 0x7ffff72a82f7 in free (/usr/local/lib64/libasan.so.5+0x1062f7)
    #1 0x94c1fd in free_proxy src/proxy.c:436
    #2 0x9355d1 in resolvers_destroy src/resolvers.c:2604
    #3 0x93e899 in resolvers_create_default src/resolvers.c:3892
    #4 0xc6ed29 in httpclient_resolve_init src/http_client.c:1170
    #5 0xc6fbcf in httpclient_create_proxy src/http_client.c:1310
    #6 0x4ae9da in ssl_ocsp_update_precheck src/ssl_ocsp.c:1452
    #7 0xa1b03f in step_init_2 src/haproxy.c:2050

But free_proxy() doesn't delete the ebpt_node that carries the name,
which perfectly explains the situation. This patch simply deletes the
name node and Stephen confirmed that it fixed the problem for him as
well. Let's also free it since the key points to p->id which is never
freed either in this function!

No backport is needed since the patch above was first merged into
3.2-dev10.
2025-04-18 23:50:13 +02:00
Amaury Denoyelle
4309a6fbf8 BUG/MINOR: quic: do not crash on CRYPTO ncbuf alloc failure
To handle out-of-order received CRYPTO frames, a ncbuf instance is
allocated. This is done via the helper quic_get_ncbuf().

Buffer allocation was improperly checked. In case b_alloc() fails, it
crashes due to a BUG_ON(). Fix this by removing it. The function now
returns NULL on allocation failure, which is already properly handled in
its caller qc_handle_crypto_frm().

This should fix the last reported crash from github issue #2935.

This must be backported up to 2.6.
2025-04-18 18:11:17 +02:00
Willy Tarreau
acd372d6ac [RELEASE] Released version 3.2-dev11
Released version 3.2-dev11 with the following main changes :
    - CI: enable weekly QuicTLS build
    - DOC: management: slightly clarify the prefix role of the '@' command
    - DOC: management: add a paragraph about the limitations of the '@' prefix
    - MINOR: master/cli: support bidirectional communications with workers
    - MEDIUM: ssl/ckch: add filename and linenum argument to crt-store parsing
    - MINOR: acme: add the acme section in the configuration parser
    - MINOR: acme: add configuration for the crt-store
    - MINOR: acme: add private key configuration
    - MINOR: acme/cli: add the 'acme renew' command
    - MINOR: acme: the acme section is experimental
    - MINOR: acme: get the ACME directory
    - MINOR: acme: handle the nonce
    - MINOR: acme: check if the account exist
    - MINOR: acme: generate new account
    - MINOR: acme: newOrder request retrieve authorizations URLs
    - MINOR: acme: allow empty payload in acme_jws_payload()
    - MINOR: acme: get the challenges object from the Auth URL
    - MINOR: acme: send the request for challenge ready
    - MINOR: acme: implement a check on the challenge status
    - MINOR: acme: generate the CSR in a X509_REQ
    - MINOR: acme: finalize by sending the CSR
    - MINOR: acme: verify the order status once finalized
    - MINOR: acme: implement retrieval of the certificate
    - BUG/MINOR: acme: ckch_conf_acme_init() when no filename
    - MINOR: ssl/ckch: handle ckch_conf in ckchs_dup() and ckch_conf_clean()
    - MINOR: acme: copy the original ckch_store
    - MEDIUM: acme: replace the previous ckch instance with new ones
    - MINOR: acme: schedule retries with a timer
    - BUILD: acme: enable the ACME feature when JWS is present
    - BUG/MINOR: cpu-topo: check the correct variable for NULL after malloc()
    - BUG/MINOR: acme: key not restored upon error in acme_res_certificate()
    - BUG/MINOR: thread: protect thread_cpus_enabled_at_boot with USE_THREAD
    - MINOR: acme: default to 2048bits for RSA
    - DOC: acme: explain how to configure and run ACME
    - BUG/MINOR: debug: remove the trailing \n from BUG_ON() statements
    - DOC: config: add the missing "profiling.memory" to the global kw index
    - DOC: config: add the missing "force-cfg-parser-pause" to the global kw index
    - DEBUG: init: report invalid characters in debug description strings
    - DEBUG: rename DEBUG_GLITCHES to DEBUG_COUNTERS and enable it by default
    - DEBUG: counters: make COUNT_IF() only appear at DEBUG_COUNTERS>=1
    - DEBUG: counters: add the ability to enable/disable updating the COUNT_IF counters
    - MINOR: tools: let dump_addr_and_bytes() support dumping before the offset
    - MINOR: debug: in call traces, dump the 8 bytes before the return address, not after
    - MINOR: debug: detect call instructions and show the branch target in backtraces
    - BUG/MINOR: acme: fix possible NULL deref
    - CLEANUP: acme: stored value is overwritten before it can be used
    - BUILD: incompatible pointer type suspected with -DDEBUG_UNIT
    - BUG/MINOR: http-ana: Properly detect client abort when forwarding the response
    - BUG/MEDIUM: http-ana: Report 502 from req analyzer only during rsp forwarding
    - CI: fedora rawhide: enable unit tests
    - DOC: configuration: fix a typo in ACME documentation
    - MEDIUM: sink: add a new dpapi ring buffer
    - Revert "BUG/MINOR: acme: key not restored upon error in acme_res_certificate()"
    - BUG/MINOR: acme: key not restored upon error in acme_res_certificate() V2
    - BUG/MINOR: acme: fix the exponential backoff of retries
    - DOC: configuration: specify limitations of ACME for 3.2
    - MINOR: acme: emit logs instead of ha_notice
    - MINOR: acme: add a success message to the logs
    - BUG/MINOR: acme/cli: fix certificate name in error message
    - MINOR: acme: register the task in the ckch_store
    - MINOR: acme: free acme_ctx once the task is done
    - BUG/MEDIUM: h3: trim whitespaces when parsing headers value
    - BUG/MEDIUM: h3: trim whitespaces in header value prior to QPACK encoding
    - BUG/MINOR: h3: filter upgrade connection header
    - BUG/MINOR: h3: reject invalid :path in request
    - BUG/MINOR: h3: reject request URI with invalid characters
    - MEDIUM: h3: use absolute URI form with :authority
    - BUG/MEDIUM: hlua: fix hlua_applet_{http,tcp}_fct() yield regression (lost data)
    - BUG/MINOR: mux-h2: prevent past scheduling with idle connections
    - BUG/MINOR: rhttp: fix reconnect if timeout connect unset
    - BUG/MINOR: rhttp: ensure GOAWAY can be emitted after reversal
    - BUG/MINOR: mux-h2: do not apply timer on idle backend connection
    - MINOR: mux-h2: refactor idle timeout calculation
    - MINOR: mux-h2: prepare to support PING emission
    - MEDIUM: server/mux-h2: implement idle-ping on backend side
    - MEDIUM: listener/mux-h2: implement idle-ping on frontend side
    - MINOR: mux-h2: do not emit GOAWAY on idle ping expiration
    - MINOR: mux-h2: handle idle-ping on conn reverse
    - BUILD: makefile: enable backtrace by default on musl
    - BUG/MINOR: threads: set threads_idle and threads_harmless even with no threads
    - BUG/MINOR debug: fix !USE_THREAD_DUMP in ha_thread_dump_fill()
    - BUG/MINOR: wdt/debug: avoid signal re-entrance between debugger and watchdog
    - BUG/MINOR: debug: detect and prevent re-entrance in ha_thread_dump_fill()
    - MINOR: debug: do not statify a few debugging functions often used with wdt/dbg
    - MINOR: tools: also protect the library name resolution against concurrent accesses
    - MINOR: tools: protect dladdr() against reentrant calls from the debug handler
    - MINOR: debug: protect ha_dump_backtrace() against risks of re-entrance
    - MINOR: tinfo: keep a copy of the pointer to the thread dump buffer
    - MINOR: debug: always reset the dump pointer when done
    - MINOR: debug: remove unused case of thr!=tid in ha_thread_dump_one()
    - MINOR: pass a valid buffer pointer to ha_thread_dump_one()
    - MEDIUM: wdt: always make the faulty thread report its own warnings
    - MINOR: debug: make ha_stuck_warning() only work for the current thread
    - MINOR: debug: make ha_stuck_warning() print the whole message at once
    - CLEANUP: debug: no longer set nor use TH_FL_DUMPING_OTHERS
    - MINOR: sched: add a new function is_sched_alive() to report scheduler's health
    - MINOR: wdt: use is_sched_alive() instead of keeping a local ctxsw copy
    - MINOR: sample: add 4 new sample fetches for clienthello parsing
    - REGTEST: add new reg-test for the 4 new clienthello fetches
    - MINOR: servers: Move the per-thread server initialization earlier
    - MINOR: proxies: Initialize the per-thread structure earlier.
    - MINOR: servers: Provide a pointer to the server in srv_per_tgroup.
    - MINOR: lb_fwrr: Move the next weight out of fwrr_group.
    - MINOR: proxies: Add a per-thread group lbprm struct.
    - MEDIUM: lb_fwrr: Use one ebtree per thread group.
    - MEDIUM: lb_fwrr: Don't start all thread groups on the same server.
    - MINOR: proxies: Do stage2 initialization for sinks too
2025-04-18 14:19:47 +02:00
Olivier Houchard
c4aec7a52f MINOR: proxies: Do stage2 initialization for sinks too
In check_config_validity(), we initialize the proxy in several stages.
We do so for the sink list for stage1, but not for stage2. It may not be
needed right now, but it may become needed in the future, so do it
anyway.
2025-04-17 17:38:23 +02:00
Olivier Houchard
658eaa4086 MEDIUM: lb_fwrr: Don't start all thread groups on the same server.
Now that all there is one tree per thread group, all thread groups will
start on the same server. To prevent that, just insert the servers in a
different order for each thread group.
2025-04-17 17:38:23 +02:00
Olivier Houchard
3758eab71c MEDIUM: lb_fwrr: Use one ebtree per thread group.
When using the round-robin load balancer, the major source of contention
is the lbprm lock, that has to be held every time we pick a server.
To mitigate that, make it so there are one tree per thread-group, and
one lock per thread-group. That means we now have a lb_fwrr_per_tgrp
structure that will contain the two lb_fwrr_groups (active and backup) as well
as the lock to protect them in the per-thread lbprm struct, and all
fields in the struct server are now moved to the per-thread structure
too.
Those changes are mostly mechanical, and brings good performances
improvment, on a 64-cores AMD CPU, with 64 servers configured, we could
process about 620000 requests par second, and we now can process around
1400000 requests per second.
2025-04-17 17:38:23 +02:00
Olivier Houchard
f36f6cfd26 MINOR: proxies: Add a per-thread group lbprm struct.
Add a new structure in the per-thread groups proxy structure, that will
contain whatever is per-thread group in lbprm.
It will be accessed as p->per_tgrp[tgid].lbprm.
2025-04-17 17:38:23 +02:00
Olivier Houchard
7ca1c94ff0 MINOR: lb_fwrr: Move the next weight out of fwrr_group.
Move the "next_weight" outside of fwrr_group, and inside struct lb_fwrr
directly, one for the active servers, one for the backup servers.
We will soon have one fwrr_group per thread group, but next_weight will
be global to all of them.
2025-04-17 17:38:23 +02:00
Olivier Houchard
444125a764 MINOR: servers: Provide a pointer to the server in srv_per_tgroup.
Add a pointer to the server into the struct srv_per_tgroup, so that if
we only have access to that srv_per_tgroup, we can come back to the
corresponding server.
2025-04-17 17:38:23 +02:00
Olivier Houchard
5e1ce09e54 MINOR: proxies: Initialize the per-thread structure earlier.
Move the call to initialize the proxy's per-thread structure earlier
than currently done, so that they are usable when we're initializing the
load balancers.
2025-04-17 17:38:23 +02:00
Olivier Houchard
e7613d3717 MINOR: servers: Move the per-thread server initialization earlier
Move the code responsible for calling per-thread server initialization
earlier than it was done, so that per-thread structures are available a
bit later, when we initialize load-balancing.
2025-04-17 17:38:23 +02:00
Mariam John
9a8c4df45d REGTEST: add new reg-test for the 4 new clienthello fetches
Add a reg-test which uses the 4 fetches:

- req.ssl_cipherlist
- req.ssl_sigalgs
- req.ssl_keyshare_groups
- req.ssl_supported_groups
2025-04-17 16:39:47 +02:00
Mariam John
fa063a9e77 MINOR: sample: add 4 new sample fetches for clienthello parsing
This patch contains this 4 new fetches and doc changes for the new fetches:

- req.ssl_cipherlist
- req.ssl_sigalgs
- req.ssl_keyshare_groups
- req.ssl_supported_groups

Towards:#2532
2025-04-17 16:39:47 +02:00
Willy Tarreau
5901164789 MINOR: wdt: use is_sched_alive() instead of keeping a local ctxsw copy
Now we can simply call is_sched_alive() on the local thread to verify
that the scheduler is still ticking instead of having to keep a copy of
the ctxsw and comparing it. It's cleaner, doesn't require to maintain
a local copy, doesn't rely on activity[] (whose purpose is mainly for
observation and debugging), and shows how this could be extended later
to cover other use cases. Practically speaking this doesn't change
anything however, the algorithm is still the same.
2025-04-17 16:25:47 +02:00
Willy Tarreau
36ec70c526 MINOR: sched: add a new function is_sched_alive() to report scheduler's health
This verifies that the scheduler is still ticking without having to
access the activity[] array nor keeping local copies of the ctxsw
counter. It just tests and sets a flag that is reset after each
return from a ->process() function.
2025-04-17 16:25:47 +02:00
Willy Tarreau
874ba2afed CLEANUP: debug: no longer set nor use TH_FL_DUMPING_OTHERS
TH_FL_DUMPING_OTHERS was being used to try to perform exclusion between
threads running "show threads" and those producing warnings. Now that it
is much more cleanly handled, we don't need that type of protection
anymore, which was adding to the complexity of the solution. Let's just
get rid of it.
2025-04-17 16:25:47 +02:00
Willy Tarreau
513397ac82 MINOR: debug: make ha_stuck_warning() print the whole message at once
It has been noticed quite a few times during troubleshooting and even
testing that warnings can happen in avalanches from multiple threads
at the same time, and that their reporting it interleaved bacause the
output is produced in small chunks. Originally, this code inspired by
the panic code aimed at making sure to log whatever could be emitted
in case it would crash later. But this approach was wrong since writes
are atomic, and performing 5 writes in sequence in each dumping thread
also means that the outputs can be mixed up at 5 different locations
between multiple threads. The output of warnings is never very long,
and the stack-based buffer is 4kB so let's just concatenate everything
in the buffer and emit it at once using a single write(). Now there's
no longer this confusion on the output.
2025-04-17 16:25:47 +02:00
Willy Tarreau
c16d5415a8 MINOR: debug: make ha_stuck_warning() only work for the current thread
Since we no longer call it with a foreign thread, let's simplify its code
and get rid of the special cases that were relying on ha_thread_dump_fill()
and synchronization with a remote thread. We're not only dumping the
current thread so ha_thread_dump_one() is sufficient.
2025-04-17 16:25:47 +02:00
Willy Tarreau
a06c215f08 MEDIUM: wdt: always make the faulty thread report its own warnings
Warnings remain tricky to deal with, especially for other threads as
they require some inter-thread synchronization that doesn't cope very
well with other parallel activities such as "show threads" for example.

However there is nothing that forces us to handle them this way. The
panic for example is already handled by bouncing the WDT signal to the
faulty thread.

This commit rearranges the WDT handler to make a better used of this
existing signal bouncing feature of the WDT handler so that it's no
longer limited to panics but can also deal with warnings. In order not
to bounce on all wakeups, we only bounce when there is a suspicion,
that is, when the warning timer has been crossed. We'll let the target
thread verify the stuck flag and context switch count by itself to
decide whether or not to panic, warn, or just do nothing and update
the counters.

As a bonus, now all warning traces look the same regardless of the
reporting thread:

   call trace(16):
   |       0x6bc733 <01 00 00 e8 6d e6 de ff]: ha_dump_backtrace+0x73/0x309 > main-0x2570
   |       0x6bd37a <00 00 00 e8 d6 fb ff ff]: ha_thread_dump_fill+0xda/0x104 > ha_thread_dump_one
   |       0x6bd625 <00 00 00 e8 7b fc ff ff]: ha_stuck_warning+0xc5/0x19e > ha_thread_dump_fill
   |       0x7b2b60 <64 8b 3b e8 00 aa f0 ff]: wdt_handler+0x1f0/0x212 > ha_stuck_warning
   | 0x7fd7e2cef3a0 <00 00 00 00 0f 1f 40 00]: libpthread:+0x123a0
   | 0x7ffc6af9e634 <85 a6 00 00 00 0f 01 f9]: linux-vdso:__vdso_gettimeofday+0x34/0x2b0
   |       0x6bad74 <7c 24 10 e8 9c 01 df ff]: sc_conn_io_cb+0x9fa4 > main-0x2400
   |       0x67c457 <89 f2 4c 89 e6 41 ff d0]: main+0x1cf147
   |       0x67d401 <48 89 df e8 8f ed ff ff]: cli_io_handler+0x191/0xb38 > main+0x1cee80
   |       0x6dd605 <40 48 8b 45 60 ff 50 18]: task_process_applet+0x275/0xce9
2025-04-17 16:25:47 +02:00
Willy Tarreau
b24d7f248e MINOR: pass a valid buffer pointer to ha_thread_dump_one()
The goal is to let the caller deal with the pointer so that the function
only has to fill that buffer without worrying about locking. This way,
synchronous dumps from "show threads" are produced and emitted directly
without causing undesired locking of the buffer nor risking causing
confusion about thread_dump_buffer containing bits from an interrupted
dump in progress.

It's only the caller that's responsible for notifying the requester of
the end of the dump by setting bit 0 of the pointer if needed (i.e. it's
only done in the debug handler).
2025-04-17 16:25:47 +02:00
Willy Tarreau
5ac739cd0c MINOR: debug: remove unused case of thr!=tid in ha_thread_dump_one()
This function was initially designed to dump any threadd into the presented
buffer, but the way it currently works is that it's always called for the
current thread, and uses the distinction between coming from a sighandler
or being called directly to detect which thread is the caller.

Let's simplify all this by replacing thr with tid everywhere, and using
the thread-local pointers where it makes sense (e.g. th_ctx, th_ctx etc).
The confusing "from_signal" argument is now replaced with "is_caller"
which clearly states whether or not the caller declares being the one
asking for the dump (the logic is inverted, but there are only two call
places with a constant).
2025-04-17 16:25:47 +02:00
Willy Tarreau
5646ec4d40 MINOR: debug: always reset the dump pointer when done
We don't need to copy the old dump pointer to the thread_dump_pointer
area anymore to indicate a dump is collected. It used to be done as an
artificial way to keep the pointer for the post-mortem analysis but
since we now have this pointer stored separately, that's no longer
needed and it simplifies the mechanim to reset it.
2025-04-17 16:25:47 +02:00
Willy Tarreau
6d8a523d14 MINOR: tinfo: keep a copy of the pointer to the thread dump buffer
Instead of using the thread dump buffer for post-mortem analysis, we'll
keep a copy of the assigned pointer whenever it's used, even for warnings
or "show threads". This will offer more opportunities to figure from a
core what happened, and will give us more freedom regarding the value of
the thread_dump_buffer itself. For example, even at the end of the dump
when the pointer is reset, the last used buffer is now preserved.
2025-04-17 16:25:47 +02:00
Willy Tarreau
d20e9cad67 MINOR: debug: protect ha_dump_backtrace() against risks of re-entrance
If a thread is dumping itself (warning, show thread etc) and another one
wants to dump the state of all threads (e.g. panic), it may interrupt the
first one during backtrace() and re-enter it from the signal handler,
possibly triggering a deadlock in the underlying libc. Let's postpone
the debug signal delivery at this point until the call ends in order to
avoid this.
2025-04-17 16:25:47 +02:00
Willy Tarreau
2dfb63313b MINOR: tools: protect dladdr() against reentrant calls from the debug handler
If a thread is currently resolving a symbol while another thread triggers
a thread dump, the current thread may enter the debug handler and call
resolve_sym_addr() again, possibly deadlocking if the underlying libc
uses locking. Let's postpone the debug signal delivery in this area
during the call. This will slow the resolution a little bit but we don't
care, it's not supposed to happen often and it must remain rock-solid.
2025-04-17 16:25:47 +02:00
Willy Tarreau
8d0c633677 MINOR: tools: also protect the library name resolution against concurrent accesses
This is an extension of eb41d768f ("MINOR: tools: use only opportunistic
symbols resolution"). It also makes sure we're not calling dladddr() in
parallel to dladdr_and_size(), as a preventive measure against some
potential deadlocks in the inner layers of the libc.
2025-04-17 16:25:47 +02:00
Willy Tarreau
5b5960359f MINOR: debug: do not statify a few debugging functions often used with wdt/dbg
A few functions are used when debugging debug signals and watchdog, but
being static, they're not resolved and are hard to spot in dumps, and
they appear as any random other function plus an offset. Let's just not
mark them static anymore, it only hurts:
  - cli_io_handler_show_threads()
  - debug_run_cli_deadlock()
  - debug_parse_cli_loop()
  - debug_parse_cli_panic()
2025-04-17 16:25:47 +02:00
Willy Tarreau
47f8397afb BUG/MINOR: debug: detect and prevent re-entrance in ha_thread_dump_fill()
In the following trace trying to abuse the watchdog from the CLI's
"debug dev loop" command running in parallel to "show threads" loops,
it's clear that some re-entrance may happen in ha_thread_dump_fill().

A first minimal fix consists in using a test-and-set on the flag
indicating that the function is currently dumping threads, so that
the one from the signal just returns. However the caller should be
made more reliable to serialize all of this, that's for future
work.

Here's an example capture of 7 threads stuck waiting for each other:
  (gdb) bt
  #0  0x00007fe78d78e147 in sched_yield () from /lib64/libc.so.6
  #1  0x0000000000674a05 in ha_thread_relax () at src/thread.c:356
  #2  0x00000000005ba4f5 in ha_thread_dump_fill (thr=2, buf=0x7ffdd8e08ab0) at src/debug.c:402
  #3  ha_thread_dump_fill (buf=0x7ffdd8e08ab0, thr=<optimized out>) at src/debug.c:384
  #4  0x00000000005baac4 in ha_stuck_warning (thr=thr@entry=2) at src/debug.c:840
  #5  0x00000000006a360d in wdt_handler (sig=<optimized out>, si=<optimized out>, arg=<optimized out>) at src/wdt.c:156
  #6  <signal handler called>
  #7  0x00007fe78d78e147 in sched_yield () from /lib64/libc.so.6
  #8  0x0000000000674a05 in ha_thread_relax () at src/thread.c:356
  #9  0x00000000005ba4c2 in ha_thread_dump_fill (thr=2, buf=0x7fe78f2d6420) at src/debug.c:426
  #10 ha_thread_dump_fill (buf=0x7fe78f2d6420, thr=2) at src/debug.c:384
  #11 0x00000000005ba7c6 in cli_io_handler_show_threads (appctx=0x2a89ab0) at src/debug.c:548
  #12 0x000000000057ea43 in cli_io_handler (appctx=0x2a89ab0) at src/cli.c:1176
  #13 0x00000000005d7885 in task_process_applet (t=0x2a82730, context=0x2a89ab0, state=<optimized out>) at src/applet.c:920
  #14 0x0000000000659002 in run_tasks_from_lists (budgets=budgets@entry=0x7ffdd8e0a5c0) at src/task.c:644
  #15 0x0000000000659bd7 in process_runnable_tasks () at src/task.c:886
  #16 0x00000000005cdcc9 in run_poll_loop () at src/haproxy.c:2858
  #17 0x00000000005ce457 in run_thread_poll_loop (data=<optimized out>) at src/haproxy.c:3075
  #18 0x0000000000430628 in main (argc=<optimized out>, argv=<optimized out>) at src/haproxy.c:3665
2025-04-17 16:25:47 +02:00
Willy Tarreau
ebf1757dc2 BUG/MINOR: wdt/debug: avoid signal re-entrance between debugger and watchdog
As seen in issue #2860, there are some situations where a watchdog could
trigger during the debug signal handler, and where similarly the debug
signal handler may trigger during the wdt handler. This is really bad
because it could trigger some deadlocks inside inner libc code such as
dladdr() or backtrace() since the code will not protect against re-
entrance but only against concurrent accesses.

A first attempt was made using ha_sigmask() but that's not always very
convenient because the second handler is called immediately after
unblocking the signal and before returning, leaving signal cascades in
backtrace. Instead, let's mark which signals to block at registration
time. Here we're blocking wdt/dbg for both signals, and optionally
SIGRTMAX if DEBUG_DEV is used as that one may also be used in this case.

This should be backported at least to 3.1.
2025-04-17 16:25:47 +02:00
Willy Tarreau
0b56839455 BUG/MINOR debug: fix !USE_THREAD_DUMP in ha_thread_dump_fill()
The function must make sure to return NULL for foreign threads and
the local buffer for the current thread in this case, otherwise panics
(and sometimes even warnings) will segfault when USE_THREAD_DUMP is
disabled. Let's slightly re-arrange the function to reduce the #if/else
since we have to specifically handle the case of !USE_THREAD_DUMP anyway.

This needs to be backported wherever b8adef065d ("MEDIUM: debug: on
panic, make the target thread automatically allocate its buf") was
backported (at least 2.8).
2025-04-17 16:25:47 +02:00
Willy Tarreau
337017e2f9 BUG/MINOR: threads: set threads_idle and threads_harmless even with no threads
Some signal handlers rely on these to decide about the level of detail to
provide in dumps, so let's properly fill the info about entering/leaving
idle. Note that for consistency with other tests we're using bitops with
t->ltid_bit, while we could simply assign 0/1 to the fields. But it makes
the code more readable and the whole difference is only 88 bytes on a 3MB
executable.

This bug is not important, and while older versions are likely affected
as well, it's not worth taking the risk to backport this in case it would
wake up an obscure bug.
2025-04-17 16:25:47 +02:00
Willy Tarreau
f499fa3dcd BUILD: makefile: enable backtrace by default on musl
The reason musl builds was not producing exploitable backtraces was
that the toolchain used appears to automatically omit the frame pointer
at -O2 but leaves it at -O0. This patch just makes sure to always append
-fno-omit-frame-pointer to the BACKTRACE cflags and enables the option
with musl where it now works. This will allow us to finally get
exploitable traces from docker images where core dumps are not always
available.
2025-04-17 16:25:47 +02:00
Amaury Denoyelle
bd1d02e2b3 MINOR: mux-h2: handle idle-ping on conn reverse
This commit extends MUX H2 connection reversal step to properly take
into account the new idle-ping feature. It first ensures that h2c task
is properly instantiated/freed depending now on both timers and
idle-ping configuration. Also, h2c_update_timeout() is now called
instead of manually requeuing the task, which ensures the proper timer
value is selected depending on the new connection side.
2025-04-17 14:49:36 +02:00
Amaury Denoyelle
cc5a7a760f MINOR: mux-h2: do not emit GOAWAY on idle ping expiration
If idle-ping is activated and h2c task is expired due to missing PING
ACK, consider that the peer is away and the connection can be closed
immediately. GOAWAY emission is thus skipped.

A new test is necessary in h2c_update_timeout() when PING ACK is
currently expected, but the next timer expiration selected is not
idle-ping. This may happen if http-keep-alive/http-request timers are
selected first. In this case, H2_CF_IDL_PING_SENT flag is resetted. This
is necessary to not prevent GOAWAY emission on expiration.
2025-04-17 14:49:36 +02:00
Amaury Denoyelle
52246249ab MEDIUM: listener/mux-h2: implement idle-ping on frontend side
This commit is the counterpart of the previous one, adapted on the
frontend side. "idle-ping" is added as keyword to bind lines, to be able
to refresh client timeout of idle frontend connections.

H2 MUX behavior remains similar as the previous patch. The only
significant change is in h2c_update_timeout(), as idle-ping is now taken
into account also for frontend connection. The calculated value is
compared with http-request/http-keep-alive timeout value. The shorter
delay is then used as expired date. As hr/ka timeout are based on
idle_start, this allows to run them in parallel with an idle-ping timer.
2025-04-17 14:49:36 +02:00
Amaury Denoyelle
a78a04cfae MEDIUM: server/mux-h2: implement idle-ping on backend side
This commit implements support for idle-ping on the backend side. First,
a new server keyword "idle-ping" is defined in configuration parsing. It
is used to set the corresponding new server member.

The second part of this commit implements idle-ping support on H2 MUX. A
new inlined function conn_idle_ping() is defined to access connection
idle-ping value. Two new connection flags are defined H2_CF_IDL_PING and
H2_CF_IDL_PING_SENT. The first one is set for idle connections via
h2c_update_timeout().

On h2_timeout_task() handler, if first flag is set, instead of releasing
the connection as before, the second flag is set and tasklet is
scheduled. As both flags are now set, h2_process_mux() will proceed to
PING emission. The timer has also been rearmed to the idle-ping value.
If a PING ACK is received before next timeout, connection timer is
refreshed. Else, the connection is released, as with timer expiration.

Also of importance, special care is needed when a backend connection is
going to idle. In this case, idle-ping timer must be rearmed. Thus a new
invokation of h2c_update_timeout() is performed on h2_detach().
2025-04-17 14:49:36 +02:00
Amaury Denoyelle
4dcfe098a6 MINOR: mux-h2: prepare to support PING emission
Adapt the already existing function h2c_ack_ping(). The objective is to
be able to emit a PING request. First, it is renamed as h2c_send_ping().
A new boolean argument <ack> is used to emit either a PING request or
ack.
2025-04-17 14:49:36 +02:00
Amaury Denoyelle
99b2e52f89 MINOR: mux-h2: refactor idle timeout calculation
Reorganize code for timeout calculation in case the connection is idle.
The objective is to better reflect the relations between each timeouts
as follow :

* if GOAWAY already emitted, use shut-timeout, or if unset fallback to
  client/server one. However, an already set timeout is never erased.

* else, for frontend connection, http-request or keep-alive timeout is
  applied depending on the current demux state. If the selected value is
  unset, fallback to client timeout

* for backend connection, no timeout is set to perform http-reuse

This commit is pure refactoring, so no functional change should occur.
2025-04-17 14:49:36 +02:00
Amaury Denoyelle
243bc95de0 BUG/MINOR: mux-h2: do not apply timer on idle backend connection
Since the following commit, MUX H2 timeout function has been slightly
exetended.

  d38d8c6ccb189e7bc813b3693fec3093c9be55f1
  BUG/MEDIUM: mux-h2: make sure control frames do not refresh the idle timeout

A side-effect of this patch is that now backend idle connection expire
timer is not reset if already defined. This means that if a timer was
registered prior to the connection transition to idle, the connection
would be destroyed on its timeout. If this happens for enough
connection, this may have an impact on the reuse rate.

In practice, this case should be rare, as h2c timer is set to
TICK_ETERNITY while there is active streams. The timer is not refreshed
most of the time before going the transition to idle, so the connection
won't be deleted on expiration.

The only case where it could occur is if there is still pending data
blocked on emission on stream detach. Here, timeout server is applied on
the connection. When the emission completes, the connection goes to
idle, but the timer will still armed, and thus will be triggered on the
idle connection.

To prevent this, explicitely reset h2c timer to TICK_ETERNITY for idle
backend connection via h2c_update_timeout().

This patch is explicitely not scheduled for backport for now, as it is
difficult to estimate the real impact of the previous code state.
2025-04-17 14:49:36 +02:00
Amaury Denoyelle
9e6f8ce328 BUG/MINOR: rhttp: ensure GOAWAY can be emitted after reversal
GOAWAY emission should not be emitted before preface. Thus, max_id field
from h2c acting as a server is initialized to -1, which prevents its
emission until preface is received from the peer. If acting as a client,
max_id is initialized to a valid value on the first h2s emission.

This causes an issue with reverse HTTP on the active side. First, it
starts as a client, so the peer does not emit a preface but instead a
simple SETTINGS frame. As role are switched, max_id is initialized much
later when the first h2s response is emitted. Thus, if the connection
must be terminated before any stream transfer, GOAWAY cannot be emitted.

To fix this, ensure max_id is initialized to 0 on h2_conn_reverse() for
active connect side. Thus, a GOAWAY indicating that no stream has been
handled can be generated.

Note that passive connect side is not impacted, as it max_id is
initialized thanks to preface reception.

This should be backported up to 2.9.
2025-04-17 14:49:36 +02:00
Amaury Denoyelle
2b8da5f9ab BUG/MINOR: rhttp: fix reconnect if timeout connect unset
Active connect on reverse http relies on connect timeout to detect
connection failure. Thus, if this timeout was unset, connection failure
may not be properly detected.

Fix this by fallback on hardcoded value of 1s for connect if timeout is
unset in the configuration. This is considered as a minor bug, as
haproxy advises against running with timeout unset.

This must be backported up to 2.9.
2025-04-17 14:49:36 +02:00
Amaury Denoyelle
3ebdd3ae50 BUG/MINOR: mux-h2: prevent past scheduling with idle connections
While reviewing HTTP/2 MUX timeout, it seems there is a possibility that
MUX task is requeued via h2c_update_timeout() with an already expired
date. This can happens with idle connections on two cases :
* first with shut timeout, as timer is not refreshed if already set
* second with http-request and keep-alive timers, which are based on
  idle_start

Queuing an already expired task is an undefined behavior. Fix this by
using task_wakeup() instead of task_queue() at the end of
h2c_update_timeout() if such case occurs.

This should be backported up to 2.6.
2025-04-17 14:49:36 +02:00
Aurelien DARRAGON
b81ab159a6 BUG/MEDIUM: hlua: fix hlua_applet_{http,tcp}_fct() yield regression (lost data)
Jacques Heunis from bloomberg reported on the mailing list [1] that
with haproxy 2.8 up to master, yielding from a Lua tcp service while
data was still buffered inside haproxy would eat some data which was
definitely lost.

He provided the reproducer below which turned out to be really helpful:

  global
      log stdout format raw local0 info
      lua-load haproxy_yieldtest.lua

  defaults
      log global
      timeout connect         10s
      timeout client          1m
      timeout server          1m

  listen echo
      bind *:9090
      mode tcp
      tcp-request content use-service lua.print_input

haproxy_yieldtest.lua:

  core.register_service("print_input", "tcp", function(applet)
      core.Info("Start printing input...")
      while true do
          local inputs = applet:getline()
          if inputs == nil or string.len(inputs) == 0 then
              core.Info("closing input connection")
              return
          end
          core.Info("Received line: "..inputs)
          core.yield()
      end
  end)

And the script below:

  #!/usr/bin/bash
  for i in $(seq 1 9999); do
      for j in $(seq 1 50); do
          echo "${i}_foo_${j}"
      done
      sleep 2
  done

Using it like this:
  ./test_seq.sh | netcat localhost 9090

We can clearly see the missing data for every "foo" burst (every 2
seconds), as they are holes in the numbering.

Thanks to the reproducer, it was quickly found that only versions
>= 2.8 were affected, and that in fact this regression was introduced
by commit 31572229e ("MEDIUM: hlua/applet: Use the sedesc to report and
detect end of processing")

In fact in 31572229e 2 mistakes were made during the refaco.
Indeed, both in hlua_applet_tcp_fct() (which is involved in the reproducer
above) and hlua_applet_http_fct(), the request (buffer) is now
systematically consumed when returning from the function, which wasn't the
case prior to this commit: when HLUA_E_AGAIN is returned, it means a
yield was requested and that the processing is not done yet, thus we
should not consume any data, like we did prior to the refacto.

Big thanks to Jacques who did a great job reproducing and reporting this
issue on the mailing list.

[1]: https://www.mail-archive.com/haproxy@formilux.org/msg45778.html

It should be backported up to 2.8 with commit 31572229e
2025-04-17 14:40:34 +02:00
Amaury Denoyelle
2c3d656f8d MEDIUM: h3: use absolute URI form with :authority
Change the representation of the start-line URI when parsing a HTTP/3
request into HTX. Adopt the same conversion as HTTP/2. If :authority
header is used (default case), the URI is encoded using absolute-form,
with scheme, host and path concatenated. If only a plain host header is
used instead, fallback to the origin form.

This commit may cause some configuration to be broken if parsing is
performed on the URI. Indeed, now most of the HTTP/3 requests will be
represented with an absolute-form URI at the stream layer.

Note that prior to this commit a check was performed on the path used as
URI to ensure that it did not contain any invalid characters. Now, this
is directly performed on the URI itself, which may include the path.

This must not be backported.
2025-04-16 18:32:00 +02:00
Amaury Denoyelle
1faa1285aa BUG/MINOR: h3: reject request URI with invalid characters
Ensure that the HTX start-line generated after parsing an HTTP/3 request
does not contain any invalid character, i.e. control or whitespace
characters.

Note that for now path is used directly as URI. Thus, the check is
performed directly over it. A patch will change this to generate an
absolute-form URI in most cases, but it won't be backported to avoid
configuration breaking in stable versions.

This must be backported up to 2.6.
2025-04-16 18:32:00 +02:00
Amaury Denoyelle
fc28fe7191 BUG/MINOR: h3: reject invalid :path in request
RFC 9114 specifies some requirements for :path pseudo-header when using
http or https scheme. This commit enforces this by rejecting a request
if needed. Thus, path cannot be empty, and it must either start with a
'/' character or contains only '*'.

This must be backported up to 2.6.
2025-04-16 18:31:55 +02:00
Amaury Denoyelle
6403bfbce8 BUG/MINOR: h3: filter upgrade connection header
As specified in RFC 9114, connection headers required special care in
HTTP/3. When a request is received with connection headers, the stream
is immediately closed. Conversely, when translating the response from
HTX, such headers are not encoded but silently ignored.

However, "upgrade" was not listed in connection headers. This commit
fixes this by adding a check on it both on request parsing and response
encoding.

This must be backported up to 2.6.
2025-04-16 18:31:04 +02:00
Amaury Denoyelle
bd3587574d BUG/MEDIUM: h3: trim whitespaces in header value prior to QPACK encoding
This commit does a similar job than the previous one, but it acts now on
the response path. Any leading or trailing whitespaces characters from a
HTX block header value are removed, prior to the header encoding via
QPACK.

This must be backported up to 2.6.
2025-04-16 18:31:04 +02:00
Amaury Denoyelle
a17e5b27c0 BUG/MEDIUM: h3: trim whitespaces when parsing headers value
Remove any leading and trailing whitespace from header field values
prior to inserting a new HTX header block. This is done when parsing a
HEADERS frame, both as headers and trailers.

This must be backported up to 2.6.
2025-04-16 18:31:04 +02:00
William Lallemand
8efafe76a3 MINOR: acme: free acme_ctx once the task is done
Free the acme_ctx task context once the task is done.
It frees everything but the config and the httpclient,
everything else is free.

The ckch_store is freed in case of error, but when the task is
successful, the ptr is set to NULL to prevent the free once inserted in
the tree.
2025-04-16 18:08:01 +02:00
William Lallemand
e778049ffc MINOR: acme: register the task in the ckch_store
This patch registers the task in the ckch_store so we don't run 2 tasks
at the same time for a given certificate.

Move the task creation under the lock and check if there was already a
task under the lock.
2025-04-16 17:12:43 +02:00
William Lallemand
115653bfc8 BUG/MINOR: acme/cli: fix certificate name in error message
The acme command had a new parameter so the certificate name is not
correct anymore because args[1] is not the certificate value anymore.
2025-04-16 17:06:52 +02:00
William Lallemand
39088a7806 MINOR: acme: add a success message to the logs
Add a success log when the certificate was updated.

Ex:

  acme: foobar.pem: Successful update of the certificate.
2025-04-16 14:51:18 +02:00
William Lallemand
31a1d13802 MINOR: acme: emit logs instead of ha_notice
Emit logs using the global logs when the ACME task failed or retries,
instead of using ha_notice().
2025-04-16 14:39:39 +02:00
William Lallemand
f36f9ca21c DOC: configuration: specify limitations of ACME for 3.2
Specify the version for which the limitation applies.
2025-04-16 14:30:45 +02:00
William Lallemand
608eb3d090 BUG/MINOR: acme: fix the exponential backoff of retries
Exponential backoff values was multiplied by 3000 instead of 3 with a
second to ms conversion. Leading to a 9000000ms value at the 2nd
attempt.

Fix the issue by setting the value in seconds and converting the value
in tick_add().

No backport needed.
2025-04-16 14:20:00 +02:00
William Lallemand
7814a8b446 BUG/MINOR: acme: key not restored upon error in acme_res_certificate() V2
When receiving the final certificate, it need to be loaded by
ssl_sock_load_pem_into_ckch(). However this function will remove any
existing private key in the struct ckch_store.

In order to fix the issue, the ptr to the key is swapped with a NULL
ptr, and restored once the new certificate is commited.

However there is a discrepancy when there is an error in
ssl_sock_load_pem_into_ckch() fails and the pointer is lost.

This patch fixes the issue by restoring the pointer in the error path.

This must fix issue #2933.
2025-04-16 14:05:04 +02:00
William Lallemand
e21a165af6 Revert "BUG/MINOR: acme: key not restored upon error in acme_res_certificate()"
This reverts commit 7a43094f8d8fe3c435ecc003f07453dd9de8134a.

Part of another incomplete patch was accidentally squash into the patch.
2025-04-16 14:03:08 +02:00
William Lallemand
bea6235629 MEDIUM: sink: add a new dpapi ring buffer
Add a 1MB ring buffer called "dpapi" for communication with the
dataplane API. It would first be used to transmit ACME informations to
the dataplane API but could be used for more.
2025-04-16 13:56:12 +02:00
William Lallemand
f6fc914fb6 DOC: configuration: fix a typo in ACME documentation
Fix "supposed" typo in ACME documentation.
2025-04-16 13:55:25 +02:00
Ilia Shipitsin
4dee087f19 CI: fedora rawhide: enable unit tests
Run the new make unit-tests on the CI.
2025-04-15 16:53:54 +02:00
Christopher Faulet
d160046e2c BUG/MEDIUM: http-ana: Report 502 from req analyzer only during rsp forwarding
A server abort must be handled by the request analyzers only when the
response forwarding was already started. Otherwise, it it the responsability
of the response analyzer to detect this event. L7-retires and conditions to
decide to silently close a client conneciotn are handled by this analyzer.

Because a reused server connections closed too early could be detected at
the wrong place, it was possible to get a 502/SH instead of a silent close,
preventing the client to safely retries its request.

Thanks to this patch, we are able to silently close the client connection in
this case and eventually to perform a L7 retry.

This patch must be backported as far as 2.8.
2025-04-15 16:28:15 +02:00
Christopher Faulet
c672b2a297 BUG/MINOR: http-ana: Properly detect client abort when forwarding the response
During the response payload forwarding, if the back SC is closed, we try to
figure out if it is because of a client abort or a server abort. However,
the condition was not accurrate, especially when abortonclose option is
set. Because of this issue, a server abort may be reported (SD-- in logs)
instead of a client abort (CD-- in logs).

The right way to detect a client abort when we try to forward the response
is to test if the back SC was shut down (SC_FL_SHUT_DOWN flag set) AND
aborted (SC_FL_ABRT_DONE flag set). When these both flags are set, it means
the back connection underwent the shutdown, which should be converted to a
client abort at this stage.

This patch should be backported as far as 2.8. It should fix last strange SD
report in the issue #2749.
2025-04-15 16:28:15 +02:00
William Lallemand
c291a5c73c BUILD: incompatible pointer type suspected with -DDEBUG_UNIT
src/jws.c: In function '__jws_init':
src/jws.c:594:38: error: passing argument 2 of 'hap_register_unittest' from incompatible pointer type [-Wincompatible-pointer-types]
  594 |         hap_register_unittest("jwk", jwk_debug);
      |                                      ^~~~~~~~~
      |                                      |
      |                                      int (*)(int,  char **)
In file included from include/haproxy/api.h:36,
                 from include/import/ebtree.h:251,
                 from include/import/ebmbtree.h:25,
                 from include/haproxy/jwt-t.h:25,
                 from src/jws.c:5:
include/haproxy/init.h:37:52: note: expected 'int (*)(void)' but argument is of type 'int (*)(int,  char **)'
   37 | void hap_register_unittest(const char *name, int (*fct)());
      |                                              ~~~~~~^~~~~~

GCC 15 is warning because the function pointer does have its
arguments in the register function.

Should fix issue #2929.
2025-04-15 15:49:44 +02:00
William Lallemand
05ebb448b5 CLEANUP: acme: stored value is overwritten before it can be used
>>>     CID 1609049:  Code maintainability issues  (UNUSED_VALUE)
   >>>     Assigning value "NULL" to "new_ckchs" here, but that stored value is overwritten before it can be used.
   592             struct ckch_store *old_ckchs, *new_ckchs = NULL;

Coverity reported an issue where a variable is initialized to NULL then
directry overwritten with another value. This doesn't arm but this patch
removes the useless initialization.

Must fix issue #2932.
2025-04-15 11:44:45 +02:00
William Lallemand
3866d3bd12 BUG/MINOR: acme: fix possible NULL deref
Task was dereferenced when setting ctx but was checked after.
This patch move the setting of ctx after the check.

Should fix issue #2931
2025-04-15 11:41:58 +02:00
Willy Tarreau
3cbbf41cd8 MINOR: debug: detect call instructions and show the branch target in backtraces
In backtraces, sometimes it's difficult to know what was called by a
given point, because some functions can be fairly long making one
doubt about the correct pointer of unresolved ones, others might
just use a tail branch instead of a call + return, etc. On common
architectures (x86 and aarch64), it's not difficult to detect and
decode a relative call, so let's do it on both of these platforms
and show the branch location after a '>'. Example:

x86_64:
   call trace(19):
   |       0x6bd644 <64 8b 38 e8 ac f7 ff ff]: debug_handler+0x84/0x95 > ha_thread_dump_one
   | 0x7feb3e5383a0 <00 00 00 00 0f 1f 40 00]: libpthread:+0x123a0
   | 0x7feb3e53748b <c0 b8 03 00 00 00 0f 05]: libpthread:__close+0x3b/0x8b
   |       0x7619e4 <44 89 ff e8 fc 97 d4 ff]: _fd_delete_orphan+0x1d4/0x1d6 > main-0x2130
   |       0x743862 <8b 7f 68 e8 8e e1 01 00]: sock_conn_ctrl_close+0x12/0x54 > fd_delete
   |       0x5ac822 <c0 74 05 4c 89 e7 ff d0]: main+0xff512
   |       0x5bc85c <48 89 ef e8 04 fc fe ff]: main+0x10f54c > main+0xff150
   |       0x5be410 <4c 89 e7 e8 c0 e1 ff ff]: main+0x111100 > main+0x10f2c0
   |       0x6ae6a4 <28 00 00 00 00 ff 51 58]: cli_io_handler+0x31524
   |       0x6aeab4 <7c 24 08 e8 fc fa ff ff]: sc_destroy+0x14/0x2a4 > cli_io_handler+0x31430
   |       0x6c685d <48 89 ef e8 43 82 fe ff]: process_chk_conn+0x51d/0x1927 > sc_destroy

aarch64:
   call trace(15):
   | 0xaaaaad0c1540 <60 6a 60 b8 c3 fd ff 97]: debug_handler+0x9c/0xbc > ha_thread_dump_one
   | 0xffffa8c177ac <c2 e0 3b d5 1f 20 03 d5]: linux-vdso:__kernel_rt_sigreturn
   | 0xaaaaad0b0964 <c0 03 5f d6 d2 ff ff 97]: cli_io_handler+0x28e44 > sedesc_new
   | 0xaaaaad0b22a4 <00 00 80 d2 94 f9 ff 97]: sc_new_from_strm+0x1c/0x54 > cli_io_handler+0x28dd0
   | 0xaaaaad0167e8 <21 00 80 52 a9 6e 02 94]: stream_new+0x258/0x67c > sc_new_from_strm
   | 0xaaaaad0b21f8 <e1 03 13 aa e7 90 fd 97]: sc_new_from_endp+0x38/0xc8 > stream_new
   | 0xaaaaacfda628 <21 18 40 f9 e7 5e 03 94]: main+0xcaca8 > sc_new_from_endp
   | 0xaaaaacfdb95c <42 c0 00 d1 02 f3 ff 97]: main+0xcbfdc > main+0xc8be0
   | 0xaaaaacfdd3f0 <e0 03 13 aa f5 f7 ff 97]: h1_io_cb+0xd0/0xb90 > main+0xcba40
2025-04-14 20:06:48 +02:00
Willy Tarreau
9740f15274 MINOR: debug: in call traces, dump the 8 bytes before the return address, not after
In call traces, we're interested in seeing the code that was executed, not
the code that was not yet. The return address is where the CPU will return
to, so we want to see the bytes that precede this location. In the example
below on x86 we can clearly see a number of direct "call" instructions
(0xe8 + 4 bytes). There are also indirect calls (0xffd0) that cannot be
exploited but it gives insights about where the code branched, which will
not always be the function above it if that one used tail branching for
example. Here's an example dump output:

         call ------------,
                          v
       0x6bd634 <64 8b 38 e8 ac f7 ff ff]: debug_handler+0x84/0x95
 0x7fa4ea2593a0 <00 00 00 00 0f 1f 40 00]: libpthread:+0x123a0
       0x752132 <00 00 00 00 00 90 41 55]: htx_remove_blk+0x2/0x354
       0x5b1a2c <4c 89 ef e8 04 07 1a 00]: main+0x10471c
       0x5b5f05 <48 89 df e8 8b b8 ff ff]: main+0x108bf5
       0x60b6f4 <89 ee 4c 89 e7 41 ff d0]: tcpcheck_eval_send+0x3b4/0x14b2
       0x610ded <00 00 00 e8 53 a5 ff ff]: tcpcheck_main+0x7dd/0xd36
       0x6c5ab4 <48 89 df e8 5c ab f4 ff]: wake_srv_chk+0xc4/0x3d7
       0x6c5ddc <48 89 f7 e8 14 fc ff ff]: srv_chk_io_cb+0xc/0x13
2025-04-14 19:28:22 +02:00
Willy Tarreau
003f5168e4 MINOR: tools: let dump_addr_and_bytes() support dumping before the offset
For code dumps, dumping from the return address is pointless, what is
interesting is to dump before the return address to read the machine
code that was executed before branching. Let's just make the function
support negative sizes to indicate that we're dumping this number of
bytes to the address instead of this number from the address. In this
case, in order to distinguish them, we're using a '<' instead of '[' to
start the series of bytes, indicating where the bytes expand and where
they stop. For example we can now see this:

       0x6bd634 <64 8b 38 e8 ac f7 ff ff]: debug_handler+0x84/0x95
 0x7fa4ea2593a0 <00 00 00 00 0f 1f 40 00]: libpthread:+0x123a0
       0x752132 <00 00 00 00 00 90 41 55]: htx_remove_blk+0x2/0x354
       0x5b1a2c <4c 89 ef e8 04 07 1a 00]: main+0x10471c
       0x5b5f05 <48 89 df e8 8b b8 ff ff]: main+0x108bf5
       0x60b6f4 <89 ee 4c 89 e7 41 ff d0]: tcpcheck_eval_send+0x3b4/0x14b2
       0x610ded <00 00 00 e8 53 a5 ff ff]: tcpcheck_main+0x7dd/0xd36
       0x6c5ab4 <48 89 df e8 5c ab f4 ff]: wake_srv_chk+0xc4/0x3d7
       0x6c5ddc <48 89 f7 e8 14 fc ff ff]: srv_chk_io_cb+0xc/0x13
2025-04-14 19:25:27 +02:00
Willy Tarreau
b708345c17 DEBUG: counters: add the ability to enable/disable updating the COUNT_IF counters
These counters can have a noticeable cost on large machines, though not
dramatic. There's no single good choice to keep them enabled or disabled.
This commit adds multiple choices:
  - DEBUG_COUNTERS set to 2 will automatically enable them by default, while
    1 will disable them by default
  - the global "debug.counters on/off" will allow to change the setting at
    boot, regardless of DEBUG_COUNTERS as long as it was at least 1.
  - the CLI "debug counters on/off" will also allow to change the value at
    run time, allowing to observe a phenomenon while it's happening, or to
    disable counters if it's suspected that their cost is too high

Finally, the "debug counters" command will append "(stopped)" at the end
of the CNT lines when these counters are stopped.

Not that the whole mechanism would easily support being extended to all
counter types by specifying the types to apply to, but it doesn't seem
useful at all and would require the user to also type "cnt" on debug
lines. This may easily be changed in the future if it's found relevant.
2025-04-14 19:02:13 +02:00
Willy Tarreau
a142adaba0 DEBUG: counters: make COUNT_IF() only appear at DEBUG_COUNTERS>=1
COUNT_IF() is convenient but can be heavy since some of them were found
to trigger often (roughly 1 counter per request on avg). This might even
have an impact on large setups due to the cost of a shared cache line
bouncing between multiple cores. For now there's no way to disable it,
so let's only enable it when DEBUG_COUNTERS is 1 or above. A future
change will make it configurable.
2025-04-14 19:02:13 +02:00
Willy Tarreau
61d633a3ac DEBUG: rename DEBUG_GLITCHES to DEBUG_COUNTERS and enable it by default
Till now the per-line glitches counters were only enabled with the
confusingly named DEBUG_GLITCHES (which would not turn glitches off
when disabled). Let's instead change it to DEBUG_COUNTERS and make sure
it's enabled by default (though it can still be disabled with
-DDEBUG_GLITCHES=0 just like for DEBUG_STRICT). It will later be
expanded to cover more counters.
2025-04-14 19:02:13 +02:00
Willy Tarreau
a8148c313a DEBUG: init: report invalid characters in debug description strings
It's easy to leave some trailing \n or even other characters that can
mangle the debug output. Let's verify at boot time that the debug sections
are clean by checking for chars 0x20 to 0x7e inclusive. This is very simple
to do and it managed to find another one in a multi-line message:

  [WARNING]  (23696) : Invalid character 0x0a at position 96 in description string at src/cli.c:2516 _send_status()

This way new offending code will be spotted before being committed.
2025-04-14 19:02:13 +02:00
Willy Tarreau
9efc60c887 DOC: config: add the missing "force-cfg-parser-pause" to the global kw index
It was documented but missing from the index, let's add it. This can be
backported to 3.1.
2025-04-14 19:02:13 +02:00
Willy Tarreau
640a699804 DOC: config: add the missing "profiling.memory" to the global kw index
It was in the description but not in the index. This can be backported to
all versions where it applies.
2025-04-14 19:02:13 +02:00
Willy Tarreau
23705564ae BUG/MINOR: debug: remove the trailing \n from BUG_ON() statements
These ones were added by mistake during the change of the cfgparse
mechanism in 3.1, but they're corrupting the output of "debug counters"
by leaving stray ']' on their own lines. We could possibly check them
all once at boot but it doens't seem worth it.

This should be backported to 3.1.
2025-04-14 19:02:13 +02:00
William Lallemand
f9390a689f DOC: acme: explain how to configure and run ACME
Add configuration about the acme section in the configuration manual, as
well as the acme command in the management guide.
2025-04-14 16:14:57 +02:00
William Lallemand
7119b5149d MINOR: acme: default to 2048bits for RSA
Change the default RSA value to 2048 bits.
2025-04-14 16:14:57 +02:00
Valentine Krasnobaeva
08efe8cd24 BUG/MINOR: thread: protect thread_cpus_enabled_at_boot with USE_THREAD
Following error is triggered at linker invokation, when we try to compile with
USE_THREAD=0 and -O0.

  make -j 8 TARGET=linux-glibc USE_LUA=1 USE_PCRE2=1 USE_LINUX_CAP=1 \
  		USE_MEMORY_PROFILING=1 OPT_CFLAGS=-O0  USE_THREAD=0

  /usr/bin/ld: src/thread.o: warning: relocation against `thread_cpus_enabled_at_boot' in read-only section `.text'
  /usr/bin/ld: src/thread.o: in function `thread_detect_count':
  /home/vk/projects/haproxy/src/thread.c:1619: undefined reference to `thread_cpus_enabled_at_boot'
  /usr/bin/ld: /home/vk/projects/haproxy/src/thread.c:1619: undefined reference to `thread_cpus_enabled_at_boot'
  /usr/bin/ld: /home/vk/projects/haproxy/src/thread.c:1620: undefined reference to `thread_cpus_enabled_at_boot'
  /usr/bin/ld: warning: creating DT_TEXTREL in a PIE
  collect2: error: ld returned 1 exit status
  make: *** [Makefile:1044: haproxy] Error 1

thread_cpus_enabled_at_boot is only available when we compiled with
USE_THREAD=1, which is the default for the most targets now.

In some cases, we need to recompile in mono-thread mode, thus
thread_cpus_enabled_at_boot should be protected with USE_THREAD in
thread_detect_count().

thread_detect_count() is always called during the process initialization
never mind of multi thread support. It sets some defaults in global.nbthread
and global.nbtgroups.

This patch is related to GitHub issue #2916.
No need to be backported as it was added in 3.2-dev9 version.
2025-04-14 16:03:21 +02:00
William Lallemand
7a43094f8d BUG/MINOR: acme: key not restored upon error in acme_res_certificate()
When receiving the final certificate, it need to be loaded by
ssl_sock_load_pem_into_ckch(). However this function will remove any
existing private key in the struct ckch_store.

In order to fix the issue, the ptr to the key is swapped with a NULL
ptr, and restored once the new certificate is commited.

However there is a discrepancy when there is an error in
ssl_sock_load_pem_into_ckch() fails and the pointer is lost.

This patch fixes the issue by restoring the pointer in the error path.

This must fix issue #2933.
2025-04-14 10:55:44 +02:00
Willy Tarreau
4a44d592ae BUG/MINOR: cpu-topo: check the correct variable for NULL after malloc()
We were testing ha_cpu_topo instead of ha_cpu_clusters after an allocation,
making the check ineffective.

No backport is needed.
2025-04-12 18:23:29 +02:00
William Lallemand
39c05cedff BUILD: acme: enable the ACME feature when JWS is present
The ACME feature depends on the JWS, which currently does not work with
every SSL libraries. This patch only enables ACME when JWS is enabled.
2025-04-12 01:39:03 +02:00
William Lallemand
a96cbe32b6 MINOR: acme: schedule retries with a timer
Schedule the retries with a 3s exponential timer. This is a temporary
mesure as the client should follow the Retry-After field for
rate-limiting for every request (https://datatracker.ietf.org/doc/html/rfc8555#section-6.6)
2025-04-12 01:39:03 +02:00
William Lallemand
768458a79e MEDIUM: acme: replace the previous ckch instance with new ones
This step is the latest to have a usable ACME certificate in haproxy.

It looks for the previous certificate, locks the "BIG CERTIFICATE LOCK",
copy every instance, deploys new ones, remove the previous one.
This is done in one step in a function which does not yield, so it could
be problematic if you have thousands of instances to handle.

It still lacks the rate limit which is mandatory to be used in
production, and more cleanup and deinit.
2025-04-12 01:39:03 +02:00
William Lallemand
9505b5bdf0 MINOR: acme: copy the original ckch_store
Copy the original ckch_store instead of creating a new one. This allows
to inherit the ckch_conf from the previous structure when doing a
ckchs_dup(). The ckch_conf contains the SAN for ACME.

Free the previous PKEY since it a new one is generated.
2025-04-12 01:39:03 +02:00
William Lallemand
5b85b81d84 MINOR: ssl/ckch: handle ckch_conf in ckchs_dup() and ckch_conf_clean()
Handle new members of the ckch_conf in ckchs_dup() and
ckch_conf_clean().

This could be automated at some point since we have the description of
all types in ckch_conf_kws.
2025-04-12 01:39:03 +02:00
William Lallemand
73ab78e917 BUG/MINOR: acme: ckch_conf_acme_init() when no filename
Does not try to strdup the configuration filename if there is none.

No backport needed.
2025-04-12 01:39:03 +02:00
William Lallemand
5500bda9eb MINOR: acme: implement retrieval of the certificate
Once the Order status is "valid", the certificate URL is accessible,
this patch implements the retrieval of the certificate which is stocked
in ctx->store.
2025-04-12 01:39:03 +02:00
William Lallemand
27fff179fe MINOR: acme: verify the order status once finalized
This implements a call to the order status to check if the certificate
is ready.
2025-04-12 01:39:03 +02:00
William Lallemand
680222b382 MINOR: acme: finalize by sending the CSR
This patch does the finalize step of the ACME task.
This encodes the CSR into base64 format and send it to the finalize URL.

https://www.rfc-editor.org/rfc/rfc8555#section-7.4
2025-04-12 01:29:27 +02:00
William Lallemand
de5dc31a0d MINOR: acme: generate the CSR in a X509_REQ
Generate the X509_REQ using the generated private key and the SAN from
the configuration. This is only done once before the task is started.

It could probably be done at the beginning of the task with the private
key generation once we have a scheduler instead of a CLI command.
2025-04-12 01:29:27 +02:00
William Lallemand
00ba62df15 MINOR: acme: implement a check on the challenge status
This patch implements a check on the challenge URL, once haproxy asked
for the challenge to be verified, it must verify the status of the
challenge resolution and if there weren't any error.
2025-04-12 01:29:27 +02:00
William Lallemand
711a13a4b4 MINOR: acme: send the request for challenge ready
This patch sends the "{}" message to specify that a challenge is ready.
It iterates on every challenge URL in the authorization list from the
acme_ctx.

This allows the ACME server to procede to the challenge validation.
https://www.rfc-editor.org/rfc/rfc8555#section-7.5.1
2025-04-12 01:29:27 +02:00
William Lallemand
ae0bc88f91 MINOR: acme: get the challenges object from the Auth URL
This patch implements the retrieval of the challenges objects on the
authorizations URLs. The challenges object contains a token and a
challenge url that need to be called once the challenge is setup.

Each authorization URLs contain multiple challenge objects, usually one
per challenge type (HTTP-01, DNS-01, ALPN-01... We only need to keep the
one that is relevent to our configuration.
2025-04-12 01:29:27 +02:00
William Lallemand
7231bf5726 MINOR: acme: allow empty payload in acme_jws_payload()
Some ACME requests are required to have a JWS with an empty payload,
let's be more flexible and allow this function to have an empty buffer.
2025-04-12 01:29:27 +02:00
William Lallemand
4842c5ea8c MINOR: acme: newOrder request retrieve authorizations URLs
This patch implements the newOrder action in the ACME task, in order to
ask for a new certificate, a list of SAN is sent as a JWS payload.
the ACME server replies a list of Authorization URLs. One Authorization
is created per SAN on a Order.

The authorization URLs are stored in a linked list of 'struct acme_auth'
in acme_ctx, so we can get the challenge URLs from them later.

The location header is also store as it is the URL of the order object.

https://datatracker.ietf.org/doc/html/rfc8555#section-7.4
2025-04-12 01:29:27 +02:00
William Lallemand
04d393f661 MINOR: acme: generate new account
The new account action in the ACME task use the same function as the
chkaccount, but onlyReturnExisting is not sent in this case!
2025-04-12 01:29:27 +02:00
William Lallemand
7f9bf4d5f7 MINOR: acme: check if the account exist
This patch implements the retrival of the KID (account identifier) using
the pkey.

A request is sent to the newAccount URL using the onlyReturnExisting
option, which allow to get the kid of an existing account.

acme_jws_payload() implement a way to generate a JWS payload using the
nonce, pkey and provided URI.
2025-04-12 01:29:27 +02:00
William Lallemand
0aa6dedf72 MINOR: acme: handle the nonce
ACME requests are supposed to be sent with a Nonce, the first Nonce
should be retrieved using the newNonce URI provided by the directory.

This nonce is stored and must be replaced by the new one received in the
each response.
2025-04-12 01:29:27 +02:00
William Lallemand
471290458e MINOR: acme: get the ACME directory
The first request of the ACME protocol is getting the list of URLs for
the next steps.

This patch implements the first request and the parsing of the response.

The response is a JSON object so mjson is used to parse it.
2025-04-12 01:29:27 +02:00
William Lallemand
4780a1f223 MINOR: acme: the acme section is experimental
Allow the usage of the acme section only when
expose-experimental-directives is set.
2025-04-12 01:29:27 +02:00
William Lallemand
b8209cf697 MINOR: acme/cli: add the 'acme renew' command
The "acme renew" command launch the ACME task for a given certificate.

The CLI parser generates a new private key using the parameters from the
acme section..
2025-04-12 01:29:27 +02:00
William Lallemand
bf6a39c4d1 MINOR: acme: add private key configuration
This commit allows to configure the generated private keys, you can
configure the keytype (RSA/ECDSA), the number of bits or the curves.

Example:

    acme LE
        uri https://acme-staging-v02.api.letsencrypt.org/directory
        account account.key
        contact foobar@example.com
        challenge HTTP-01
        keytype ECDSA
        curves P-384
2025-04-12 01:29:27 +02:00
William Lallemand
2e8c350b95 MINOR: acme: add configuration for the crt-store
Add new acme keywords for the ckch_conf parsing, which will be used on a
crt-store, a crt line in a frontend, or even a crt-list.

The cfg_postparser_acme() is called in order to check if a section referenced
elsewhere really exists in the config file.
2025-04-12 01:29:27 +02:00
William Lallemand
077e2ce84c MINOR: acme: add the acme section in the configuration parser
Add a configuration parser for the new acme section, the section is
configured this way:

    acme letsencrypt
        uri https://acme-staging-v02.api.letsencrypt.org/directory
        account account.key
        contact foobar@example.com
        challenge HTTP-01

When unspecified, the challenge defaults to HTTP-01, and the account key
to "<section_name>.account.key".

Section are stored in a linked list containing acme_cfg structures, the
configuration parsing is mostly resolved in the postsection parser
cfg_postsection_acme() which is called after the parsing of an acme section.
2025-04-12 01:29:27 +02:00
William Lallemand
20718f40b6 MEDIUM: ssl/ckch: add filename and linenum argument to crt-store parsing
Add filename and linenum arguments to the crt-store / ckch_conf parsing.

It allows to use them in the parsing function so we could emits error.
2025-04-12 01:29:27 +02:00
Willy Tarreau
00c967fac4 MINOR: master/cli: support bidirectional communications with workers
Some rare commands in the worker require to keep their input open and
terminate when it's closed ("show events -w", "wait"). Others maintain
a per-session context ("set anon on"). But in its default operation
mode, the master CLI passes commands one at a time to the worker, and
closes the CLI's input channel so that the command can immediately
close upon response. This effectively prevents these two specific cases
from being used.

Here the approach that we take is to introduce a bidirectional mode to
connect to the worker, where everything sent to the master is immediately
forwarded to the worker (including the raw command), allowing to queue
multiple commands at once in the same session, and to continue to watch
the input to detect when the client closes. It must be a client's choice
however, since doing so means that the client cannot batch many commands
at once to the master process, but must wait for these commands to complete
before sending new ones. For this reason we use the prefix "@@<pid>" for
this. It works exactly like "@" except that it maintains the channel
open during the whole execution. Similarly to "@<pid>" with no command,
"@@<pid>" will simply open an interactive CLI session to the worker, that
will be ended by "quit" or by closing the connection. This can be convenient
for the user, and possibly for clients willing to dedicate a connection to
the worker.
2025-04-11 16:09:17 +02:00
Willy Tarreau
b6a8abcd0b DOC: management: add a paragraph about the limitations of the '@' prefix
The '@' prefix permits to execute a single command at once in a worker.
It is very handy but comes with some limitations affecting rare commands,
which is better to be documented (one command per session, input closed)
since it can seldom have user-visible effects.
2025-04-11 16:09:17 +02:00
Willy Tarreau
e8267d1ce2 DOC: management: slightly clarify the prefix role of the '@' command
While the examples were clear, the text did not fully imply what was
reflected there. Better have the text explicitly mention that the
'@' command may be used as a prefix or wrapper in front of a command
as well as a standalone command.
2025-04-11 16:09:17 +02:00
Ilya Shipitsin
eed4116c07 CI: enable weekly QuicTLS build
QuicTLS started own fork not dependant on OpenSSL, lets add
that to weekly builds

ML: https://www.mail-archive.com/haproxy@formilux.org/msg45574.html
GH: https://github.com/quictls/quictls/issues/244
2025-04-11 16:01:45 +02:00
Willy Tarreau
a6982a898e [RELEASE] Released version 3.2-dev10
Released version 3.2-dev10 with the following main changes :
    - REORG: ssl: move curves2nid and nid2nist to ssl_utils
    - BUG/MEDIUM: stream: Fix a possible freeze during a forced shut on a stream
    - MEDIUM: stream: Save SC and channel flags earlier in process_steam()
    - BUG/MINOR: peers: fix expire learned from a peer not converted from ms to ticks
    - BUG/MEDIUM: peers: prevent learning expiration too far in futur from unsync node
    - CI: spell check: allow manual trigger
    - CI: codespell: add "pres" to spellcheck whitelist
    - CLEANUP: assorted typo fixes in the code, commits and doc
    - CLEANUP: atomics: remove support for gcc < 4.7
    - CLEANUP: atomics: also replace __sync_synchronize() with __atomic_thread_fence()
    - TESTS: Fix build for filltab25.c
    - MEDIUM: ssl: replace "crt" lines by "ssl-f-use" lines
    - DOC: configuration: replace "crt" by "ssl-f-use" in listeners
    - MINOR: backend: mark srv as nonnull in alloc_dst_address()
    - BUG/MINOR: server: ensure check-reuse-pool is copied from default-server
    - MINOR: server: activate automatically check reuse for rhttp@ protocol
    - MINOR: check/backend: support conn reuse with SNI
    - MINOR: check: implement check-pool-conn-name srv keyword
    - MINOR: task: add thread safe notification_new and notification_wake variants
    - BUG/MINOR: hlua_fcn: fix potential UAF with Queue:pop_wait()
    - MINOR: hlua_fcn: register queue class using hlua_register_metatable()
    - MINOR: hlua: add core.wait()
    - MINOR: hlua: core.wait() takes optional delay paramater
    - MINOR: hlua: split hlua_applet_tcp_recv_yield() in two functions
    - MINOR: hlua: add AppletTCP:try_receive()
    - MINOR: hlua_fcn: add Queue:alarm()
    - MEDIUM: task: make notification_* API thread safe by default
    - CLEANUP: log: adjust _lf_cbor_encode_byte() comment
    - MEDIUM: ssl/crt-list: warn on negative wildcard filters
    - MEDIUM: ssl/crt-list: warn on negative filters only
    - BUILD: atomics: fix build issue on non-x86/non-arm systems
    - BUG/MINOR: log: fix CBOR encoding with LOG_VARTEXT_START() + lf_encode_chunk()
    - BUG/MEDIUM: sample: fix risk of overflow when replacing multiple regex back-refs
    - DOC: configuration: rework the crt-list section
    - MINOR: ring: support arbitrary delimiters through ring_dispatch_messages()
    - MINOR: ring/cli: support delimiting events with a trailing \0 on "show events"
    - DEV: h2: fix h2-tracer.lua nil value index
    - BUG/MINOR: backend: do not use the source port when hashing clientip
    - BUG/MINOR: hlua: fix invalid errmsg use in hlua_init()
    - MINOR: proxy: add setup_new_proxy() function
    - MINOR: checks: mark CHECKS-FE dummy frontend as internal
    - MINOR: flt_spoe: mark spoe agent frontend as internal
    - MEDIUM: tree-wide: avoid manually initializing proxies
    - MINOR: proxy: add deinit_proxy() helper func
    - MINOR: checks: deinit checks_fe upon deinit
    - MINOR: flt_spoe: deinit spoe agent proxy upon agent release
2025-04-11 10:04:00 +02:00
Aurelien DARRAGON
f3b231714f MINOR: flt_spoe: deinit spoe agent proxy upon agent release
Even though spoe agent proxy is statically allocated, it uses the proxy
API and is initialized like a regular proxy, thus specific cleanup is
required upon release. This is not tagged as a bug because as of now this
would only cause some minor memory leak upon deinit.

We check the presence of proxy->id to know if it was initialized since
we cannot rely on a pointer for that.
2025-04-10 22:10:31 +02:00
Aurelien DARRAGON
8a944d0e46 MINOR: checks: deinit checks_fe upon deinit
This is just to make valgrind and friends happy, leverage deinit_proxy()
for checks_fe proxy upon deinit to ensure proper cleanup.

We check the presence of proxy->id to know if it was initialized because
we cannot rely on a pointer for that.
2025-04-10 22:10:31 +02:00
Aurelien DARRAGON
fbfeb591f7 MINOR: proxy: add deinit_proxy() helper func
Same as free_proxy(), but does not free the base proxy pointer (ie: the
proxy itself may not be allocated)

Goal is to be able to cleanup statically allocated dummy proxies.
2025-04-10 22:10:31 +02:00
Aurelien DARRAGON
4194f756de MEDIUM: tree-wide: avoid manually initializing proxies
In this patch we try to use the proxy API init functions as much as
possible to avoid code redundancy and prevent proxy initialization
errors. As such, we prefer using alloc_new_proxy() and setup_new_proxy()
instead of manually allocating the proxy pointer and performing the
base init ourselves.
2025-04-10 22:10:31 +02:00
Aurelien DARRAGON
60f45564a1 MINOR: flt_spoe: mark spoe agent frontend as internal
spoe agent frontend is used by the agent internally, but it is not meant
to be directly exposed like user-facing proxies defined in the config.

As such, better mark it as internal using PR_CAP_INT capability to prevent
any mis-use.
2025-04-10 22:10:31 +02:00
Aurelien DARRAGON
5087048b6d MINOR: checks: mark CHECKS-FE dummy frontend as internal
CHECKS-FE frontend is a dummy frontend used to create checks sessions
as such, it is internal and should not be exposed to the user.
Better mark it as internal using PR_CAP_INT capability to prevent
proxy API from ever exposing it.
2025-04-10 22:10:31 +02:00
Aurelien DARRAGON
e1cec655ee MINOR: proxy: add setup_new_proxy() function
Split alloc_new_proxy() in two functions: the preparing part is now
handled by setup_new_proxy() which can be called individually, while
alloc_new_proxy() takes care of allocating a new proxy struct and then
calling setup_new_proxy() with the freshly allocated proxy.
2025-04-10 22:10:31 +02:00
Aurelien DARRAGON
ea3c96369f BUG/MINOR: hlua: fix invalid errmsg use in hlua_init()
errmsg is used with memprintf and friends, thus it must be NULL
initialized before being passed to memprintf, else invalid read will
occur.

However in hlua_init() the errmsg value isn't initialized, let's fix that

This is really minor because it would only cause issue on error paths,
yet it may be backported to all stable versions, just in case.
2025-04-10 22:10:26 +02:00
Willy Tarreau
7b6df86a83 BUG/MINOR: backend: do not use the source port when hashing clientip
The server's "usesrc" keyword supports among other options "client"
and "clientip". The former means we bind to the client's IP and port
to connect to the server, while the latter means we bind to its IP
only. It's done in two steps, first alloc_bind_address() retrieves
the IP address and port, and second, tcp_connect_server() decides
to either bind to the IP only or IP+port.

The problem comes with idle connection pools, which hash all the
parameters: the hash is calculated before (and ideally withouy) calling
tcp_connect_server(), and it considers the whole struct sockaddr_storage
for the hash, except that both client and clientip entirely fill it with
the client's address. This means that both client and clientip make use
of the source port in the hash calculation, making idle connections
almost not reusable when using "usesrc clientip" while they should for
clients coming from the same source. A work-around is to force the
source port to zero using "tcp-request session set-src-port int(0)" but
it's ugly.

Let's fix this by properly zeroing the port for AF_INET/AF_INET6 addresses.

This can be backported to 2.4. Thanks to Sebastien Gross for providing a
reproducer for this problem.
2025-04-09 11:05:22 +02:00
Aurelien DARRAGON
afd5f5d671 DEV: h2: fix h2-tracer.lua nil value index
Nick Ramirez reported the following error while testing the h2-tracer.lua
script:

  Lua filter 'h2-tracer' : [state-id 0] runtime error: /etc/haproxy/h2-tracer.lua:227: attempt to index a nil value (field '?') from /etc/haproxy/h2-tracer.lua:227: in function line 109.

It is caused by h2ff indexing with an out of bound value. Indeed, h2ff
is indexed with the frame type, which can potentially be > 9 (not common
nor observed during Willy's tests), while h2ff only defines indexes from
0 to 9.

The fix was provided by Willy, it consists in skipping h2ff indexing if
frame type is > 9. It was confirmed that doing so fixes the error.
2025-04-08 17:44:41 +02:00
Willy Tarreau
f4634e5a38 MINOR: ring/cli: support delimiting events with a trailing \0 on "show events"
At the moment it is not supported to produce multi-line events on the
"show events" output, simply because the LF character is used as the
default end-of-event mark. However it could be convenient to produce
well-formatted multi-line events, e.g. in JSON or other formats. UNIX
utilities have already faced similar needs in the past and added
"-print0" to "find" and "-0" to "xargs" to mention that the delimiter
is the NUL character. This makes perfect sense since it's never present
in contents, so let's do exactly the same here.

Thus from now on, "show events <ring> -0" will delimit messages using
a \0 instead of a \n, permitting a better and safer encapsulation.
2025-04-08 14:36:35 +02:00
Willy Tarreau
0be6d73e88 MINOR: ring: support arbitrary delimiters through ring_dispatch_messages()
In order to support delimiting output events with other characters than
just the LF, let's pass the delimiter through the API. The default remains
the LF, used by applet_append_line(), and ignored by the log forwarder.
2025-04-08 14:36:35 +02:00
William Lallemand
038a372684 DOC: configuration: rework the crt-list section
The crt-list section was unclear, this patch reworks it, giving more
details on the matching algorithms and how the things are loaded.
2025-04-08 14:29:10 +02:00
Willy Tarreau
3e3b9eebf8 BUG/MEDIUM: sample: fix risk of overflow when replacing multiple regex back-refs
Aleandro Prudenzano of Doyensec and Edoardo Geraci of Codean Labs
reported a bug in sample_conv_regsub(), which can cause replacements
of multiple back-references to overflow the temporary trash buffer.

The problem happens when doing "regsub(match,replacement,g)": we're
replacing every occurrence of "match" with "replacement" in the input
sample, which requires a length check. For this, a max is applied, so
that a replacement may not use more than the remaining length in the
buffer. However, the length check is made on the replaced pattern and
not on the temporary buffer used to carry the new string. This results
in the remaining size to be usable for each input match, which can go
beyond the temporary buffer size if more than one occurrence has to be
replaced with something that's larger than the remaining room.

The fix proposed by Aleandro and Edoardo is the correct one (check on
"trash" not "output"), and is the one implemented in this patch.

While it is very unlikely that a config will replace multiple short
patterns each with a larger one in a request, this possibility cannot
be entirely ruled out (e.g. mask a known, short IP address using
"XXX.XXX.XXX.XXX").  However when this happens, the replacement pattern
will be static, and not be user-controlled, which is why this patch is
marked as medium.

The bug was introduced in 2.2 with commit 07e1e3c93e ("MINOR: sample:
regsub now supports backreferences"), so it must be backported to all
versions.

Special thanks go to Aleandro and Edoardo for reporting this bug with
a simple reproducer and a fix.
2025-04-07 15:57:28 +02:00
Aurelien DARRAGON
9e8444b730 BUG/MINOR: log: fix CBOR encoding with LOG_VARTEXT_START() + lf_encode_chunk()
There have been some reports that using %HV logformat alias with CBOR
encoder would produce invalid CBOR payload according to some CBOR
implementations such as "cbor.me". Indeed, with the below log-format:

  log-format "%{+cbor}o %(protocol)HV"

And the resulting CBOR payload:

  BF6870726F746F636F6C7F48485454502F312E31FFFF

cbor.me would complain with: "bytes/text mismatch (ASCII-8BIT != UTF-8) in
streaming string") error message.

It is due to the version string being first announced as text, while CBOR
encoder actually encodes it as byte string later when lf_encode_chunk()
is used.

In fact it affects all patterns combining LOG_VARTEXT_START() with
lf_encode_chunk() which means  %HM, %HU, %HQ, %HPO and %HP are also
affected. To fix the issue, in _lf_encode_bytes() (which is
lf_encode_chunk() helper), we now check if we are inside a VARTEXT (we
can tell it if ctx->in_text is true), in which case we consider that we
already announced the current data as regular text so we keep the same
type to encode the bytes from the chunk to prevent inconsistencies.

It should be backported in 3.0
2025-04-07 12:27:14 +02:00
Willy Tarreau
f01ff2478f BUILD: atomics: fix build issue on non-x86/non-arm systems
Commit f435a2e518 ("CLEANUP: atomics: also replace __sync_synchronize()
with __atomic_thread_fence()") replaced the builtins used for barriers,
but the different API required an argument while the macros didn't specify
any, resulting in double parenthesis that were causing obscure build errors
such as "called object type 'void' is not a function or function pointer".
Let's just specify the args for the macro. No backport is needed.
2025-04-07 09:38:22 +02:00
William Lallemand
ab4cd49c04 MEDIUM: ssl/crt-list: warn on negative filters only
negative SNI filters on crt-list lines only have a meaning when they
match a positive wildcard filter. This patch adds a warning which
is emitted when trying to use negative filters without any wildcard on
the same line.

This was discovered in ticket #2900.
2025-04-04 18:18:44 +02:00
William Lallemand
a9ae6b516d MEDIUM: ssl/crt-list: warn on negative wildcard filters
negative wildcard filters were always a noop, and are not useful for
anything unless you want to use !* alone to remove every name from a
certificate.

This is confusing and the documentation never stated it correctly. This
patch adds a warning during the bind initialization if it founds one,
only !* does not emit a warning.

This patch was done during the debugging of issue #2900.
2025-04-04 17:13:51 +02:00
Aurelien DARRAGON
ce6951d6f9 CLEANUP: log: adjust _lf_cbor_encode_byte() comment
_lf_cbor_encode_byte() comment was not updated in c33b857df ("MINOR: log:
support true cbor binary encoding") to reflect the new behavior.

Indeed, binary form is now supported. Updating the comment that says
otherwise.
2025-04-03 17:52:56 +02:00
Aurelien DARRAGON
11d4d0957e MEDIUM: task: make notification_* API thread safe by default
Some notification_* functions were not thread safe by default as they
assumed only one producer would emit events for registered tasks.

While this suited well with the Lua sockets use-case, this proved to
be a limitation with some other event sources (ie: lua Queue class)

instead of having to deal with both the non thread safe and thread
safe variants (_mt suffix), which is error prone, let's make the
entire API thread safe regarding the event list.

Pruning functions still require that only one thread executes them,
with Lua this is always the case because there is one cleanup list
per context.
2025-04-03 17:52:50 +02:00
Aurelien DARRAGON
976890edda MINOR: hlua_fcn: add Queue:alarm()
Queue:alarm() sets a wakeup alarm on the task when new data becomes
available on Queue. It must be re-armed for each event.

Lua documentation was updated
2025-04-03 17:52:44 +02:00
Aurelien DARRAGON
0ffc80d3ba MINOR: hlua: add AppletTCP:try_receive()
This is the non-blocking variant for AppletTCP:receive(). It doesn't
take any argument, instead it tries to read as much data as available
at once. If no data is available, empty string is returned.

Lua documentation was updated.
2025-04-03 17:52:39 +02:00
Aurelien DARRAGON
86d3cfdeeb MINOR: hlua: split hlua_applet_tcp_recv_yield() in two functions
Split hlua_applet_tcp_recv_yield() in order to create
hlua_applet_tcp_recv_try() helper function which does a single receive
attempt.
2025-04-03 17:52:34 +02:00
Aurelien DARRAGON
c7cbfafa38 MINOR: hlua: core.wait() takes optional delay paramater
core.wait() now accepts optional delay parameter in ms. Passed this delay
the task is woken up if no event woke the task before.

Lua documentation was updated.
2025-04-03 17:52:28 +02:00
Aurelien DARRAGON
1e4e5ab4d2 MINOR: hlua: add core.wait()
Similar to core.yield(), except that the task is not woken up
automatically, instead it waits for events to trigger the task
wakeup.

Lua documentation was updated.
2025-04-03 17:52:23 +02:00
Aurelien DARRAGON
748dba4859 MINOR: hlua_fcn: register queue class using hlua_register_metatable()
Most lua classes are registered by leveraging the
hlua_register_metatable() helper. Let's use that for the Queue class as
well for consitency.
2025-04-03 17:52:17 +02:00
Aurelien DARRAGON
c6fa061f22 BUG/MINOR: hlua_fcn: fix potential UAF with Queue:pop_wait()
If Queue:pop_wait() excecuted from a stream context and pop_wait() is
aborted due to a Lua or ressource error, then the waiting object pointing
to the task will still be registered, so if the task eventually dissapears,
Queue:push() may try to wake invalid task pointer..

To prevent this bug from happening, we now rely on notification_* API to
deliver waiting signals. This way signals are properly garbage collected
when a lua context is destroyed.

It should be backported in 2.8 with 86fb22c55 ("MINOR: hlua_fcn: add Queue
class").
This patch depends on ("MINOR: task: add thread safe notification_new and
notification_wake variants")
2025-04-03 17:52:09 +02:00
Aurelien DARRAGON
b77b1a2c3a MINOR: task: add thread safe notification_new and notification_wake variants
notification_new and notification_wake were historically meant to be
called by a single thread doing both the init and the wakeup for other
tasks waiting on the signals.

In this patch, we extend the API so that notification_new and
notification_wake have thread-safe variants that can safely be used with
multiple threads registering on the same list of events and multiple
threads pushing updates on the list.
2025-04-03 17:52:03 +02:00
Amaury Denoyelle
f0f1816f1a MINOR: check: implement check-pool-conn-name srv keyword
This commit is a direct follow-up of the previous one. It defines a new
server keyword check-pool-conn-name. It is used as the default value for
the name parameter of idle connection hash generation.

Its behavior is similar to server keyword pool-conn-name, but reserved
for checks reuse. If check-pool-conn-name is set, it is used in priority
to match a connection for reuse. If unset, a fallback is performed on
check-sni.
2025-04-03 17:19:07 +02:00
Amaury Denoyelle
43367f94f1 MINOR: check/backend: support conn reuse with SNI
Support for connection reuse during server checks was implemented
recently. This is activated with the server keyword check-reuse-pool.

Similarly to stream processing via connect_backend(), a connection hash
is calculated when trying to perform reuse for checks. This is necessary
to retrieve for a connection which shares the check connect parameters.
However, idle connections can additionnally be tagged using a
pool-conn-name or SNI under connect_backend(). Check reuse does not test
these values, which prevent to retrieve a matching connection.

Improve this by using "check-sni" value as idle connection hash input
for check reuse. be_calculate_conn_hash() API has been adjusted so that
name value can be passed as input, both when using streams or checks.

Even with the current patch, there is still some scenarii which could
not be covered for checks connection reuse. most notably, when using
dynamic pool-conn-name/SNI value. It is however at least sufficient to
cover simpler cases.
2025-04-03 17:19:07 +02:00
Amaury Denoyelle
28116e307a MINOR: server: activate automatically check reuse for rhttp@ protocol
Without check-reuse-pool, it is impossible to perform check on server
using @rhttp protocol. This is due to the inherent nature of the
protocol which does not implement an active connect method.

Thus, ensure that check-reuse-pool is always set when a reverse HTTP
server is declared. This reduces server configuration and should prevent
any omission. Note that it is still require to add "check" server
keyword so activate server checks.
2025-04-03 17:19:07 +02:00
Amaury Denoyelle
ace9f5db10 BUG/MINOR: server: ensure check-reuse-pool is copied from default-server
Duplicate server check.reuse_pool boolean value in srv_settings_cpy().
This is necessary to ensure that check-reuse-pool value can be set via
default-server or server-template.

This does not need to be backported.
2025-04-03 17:19:07 +02:00
Amaury Denoyelle
76e9156c9b MINOR: backend: mark srv as nonnull in alloc_dst_address()
Server instance can be NULL on connect_server(), either when dispatch or
transparent proxy are active. However, in alloc_dst_address() access to
<srv> is safe thanks to SF_ASSIGNED stream flag. Add an ASSUME_NONNULL()
to reflect this state.

This should fix coverity report from github issue #2922.
2025-04-03 17:19:07 +02:00
William Lallemand
feb1a9ea17 DOC: configuration: replace "crt" by "ssl-f-use" in listeners
Replace the "crt" keyword from the frontend section with a "ssl-f-use"
keyword, "crt" could be ambigous in case we don't want to put a
certificate filename.
2025-04-03 16:38:15 +02:00
William Lallemand
c7f29afcea MEDIUM: ssl: replace "crt" lines by "ssl-f-use" lines
The new "crt" lines in frontend and listen sections are confusing:

- a filename is mandatory but we could need a syntax without the
  filename in the future, if the filename is generated for example
- there is no clue about the fact that its only used on the frontend
  side when reading the line

A new "ssl-f-use" line replaces the "crt" line, but a "crt" keyword
can be used on this line. "f" indicates that this is the frontend
configuration, a "ssl-b-use" keyword could be used in the future.

The "crt" lines only appeared in 3.2-dev so this won't change anything
for people using configurations from previous major versions.
2025-04-03 16:38:15 +02:00
Olivier Houchard
4715c557e9 TESTS: Fix build for filltab25.c
Give a return type to main(), so that filltab25.c compiles with
modern compilers.
2025-04-03 15:59:41 +02:00
Willy Tarreau
f435a2e518 CLEANUP: atomics: also replace __sync_synchronize() with __atomic_thread_fence()
The drop of older compilers also allows us to focus on clearer
barriers, so let's use them.
2025-04-03 11:59:31 +02:00
Willy Tarreau
34e3b83f9c CLEANUP: atomics: remove support for gcc < 4.7
The old __sync_* API is no longer necessary since we do not support
gcc before 4.7 anymore. Let's just get rid of this code, the file is
still ugly enough without it.
2025-04-03 11:55:35 +02:00
Ilia Shipitsin
27a6353ceb CLEANUP: assorted typo fixes in the code, commits and doc 2025-04-03 11:37:25 +02:00
Ilia Shipitsin
bd477d5f51 CI: codespell: add "pres" to spellcheck whitelist
spellcheck was triggered by the following:

  * pres  : same as "res" but using the parent stream, if any. "pres"
            variables are only accessible during response processing of the
            parent stream.
2025-04-03 11:37:25 +02:00
Ilia Shipitsin
30df5b0f23 CI: spell check: allow manual trigger 2025-04-03 11:37:25 +02:00
Emeric Brun
b02b8453d1 BUG/MEDIUM: peers: prevent learning expiration too far in futur from unsync node
This patch sets the expire of the entry to the max value in
configuration if the value showed in the peer update message
is too far in futur.

This should be backported an all supported branches.
2025-04-03 11:26:29 +02:00
Emeric Brun
00461fbfbf BUG/MINOR: peers: fix expire learned from a peer not converted from ms to ticks
This is has now impact currently since MS_TO_TICKS macro does nothing
but it will prevent further bugs.
2025-04-03 11:26:21 +02:00
Christopher Faulet
6365eb85e5 MEDIUM: stream: Save SC and channel flags earlier in process_steam()
At the begining of process_stream(), the flags of the stream connectors and
channels are saved to be able to handle changes performed in sub-functions
(for instance in analyzers). But, some operations were performed before
saving these flags: Synchronous receives and forced shutdowns. While it
seems to safe for now, it is a bit annoying because some events could be
missed.

So, to avoid bugs in the future, the channels and stream connectors flags
are now really saved before any other processing.
2025-04-03 10:19:58 +02:00
Christopher Faulet
51611a5b70 BUG/MEDIUM: stream: Fix a possible freeze during a forced shut on a stream
When a forced shutdown is performed on a stream, it is possible to freeze it
infinitly because it is performed in an unexpected way from process_stream()
point of view, especially when the stream is waiting for a server
connection. The events sequence is a bit complex but at the end the stream
remains blocked in turn-around state and no event are trriggered to unblock
it.

By trying to fix the issue, we considered it was safer to rethink the
feature. The idea is to quickly shutdown a stream to release resources. For
instance to be able to delete a server. So, instead of scheduling a
shutdown, it is more efficient to trigger an error and detach the stream
from the server, if neecessary. The same code than the one used to deal with
connection errors in back_handle_st_cer() is used.

This patch must be slowly backported as far as 2.6.
2025-04-03 10:19:57 +02:00
William Lallemand
b351f06ff1 REORG: ssl: move curves2nid and nid2nist to ssl_utils
curves2nid and nid2nist are generic functions that could be used outside
the JWS scope, this patch put them at the right place so they can be
reused.
2025-04-02 19:34:09 +02:00
Willy Tarreau
a8fab63604 [RELEASE] Released version 3.2-dev9
Released version 3.2-dev9 with the following main changes :
    - MINOR: quic: move global tune options into quic_tune
    - CLEANUP: quic: reorganize TP flow-control initialization
    - MINOR: quic: ignore uni-stream for initial max data TP
    - MINOR: mux-quic: define config for max-data
    - MINOR: quic: define max-stream-data configuration as a ratio
    - MEDIUM: lb-chash: add directive hash-preserve-affinity
    - MEDIUM: pools: be a bit smarter when merging comparable size pools
    - REGTESTS: disable the test balance/balance-hash-maxqueue
    - BUG/MINOR: log: fix gcc warn about truncating NUL terminator while init char arrays
    - CI: fedora rawhide: allow "on: workflow_dispatch" in forks
    - CI: fedora rawhide: install "awk" as a dependency
    - CI: spellcheck: allow "on: workflow_dispatch" in forks
    - CI: coverity scan: allow "on: workflow_dispatch" in forks
    - CI: cross compile: allow "on: workflow_dispatch" in forks
    - CI: Illumos: allow "on: workflow_dispatch" in forks
    - CI: NetBSD: allow "on: workflow_dispatch" in forks
    - CI: QUIC Interop on AWS-LC: allow "on: workflow_dispatch" in forks
    - CI: QUIC Interop on LibreSSL: allow "on: workflow_dispatch" in forks
    - MINOR: compiler: add __nonstring macro
    - MINOR: thread: dump the CPU topology in thread_map_to_groups()
    - MINOR: cpu-set: compare two cpu sets with ha_cpuset_isequal()
    - MINOR: cpu-set: add a new function to print cpu-sets in human-friendly mode
    - MINOR: cpu-topo: add a dump of thread-to-CPU mapping to -dc
    - MINOR: cpu-topo: pass an extra argument to ha_cpu_policy
    - MINOR: cpu-topo: add new cpu-policies "group-by-2-clusters" and above
    - BUG/MINOR: config: silence .notice/.warning/.alert in discovery mode
    - EXAMPLES: add "games.cfg" and an example game in Lua
    - MINOR: jws: emit the JWK thumbprint
    - TESTS: jws: change the jwk format
    - MINOR: ssl/ckch: add substring parser for ckch_conf
    - MINOR: mt_list: Implement mt_list_try_lock_prev().
    - MINOR: lbprm: Add method to deinit server and proxy
    - MINOR: threads: Add HA_RWLOCK_TRYRDTOWR()
    - MAJOR: leastconn; Revamp the way servers are ordered.
    - BUG/MINOR: ssl/ckch: leak in error path
    - BUILD: ssl/ckch: potential null pointer dereference
    - MINOR: log: support "raw" logformat node typecast
    - CLEANUP: assorted typo fixes in the code and comments
    - DOC: config: fix two missing "content" in "tcp-request" examples
    - MINOR: cpu-topo: cpu_dump_topology() SMT info check little optimisation
    - BUILD: compiler: undefine the CONCAT() macro if already defined
    - BUG/MEDIUM: leastconn: Don't try to reposition if the server is down
    - BUG/MINOR: rhttp: fix incorrect dst/dst_port values
    - BUG/MINOR: backend: do not overwrite srv dst address on reuse
    - BUG/MEDIUM: backend: fix reuse with set-dst/set-dst-port
    - MINOR: sample: define bc_reused fetch
    - REGTESTS: extend conn reuse test with transparent proxy
    - MINOR: backend: fix comment when killing idle conns
    - MINOR: backend: adjust conn_backend_get() API
    - MINOR: backend: extract conn hash calculation from connect_server()
    - MINOR: backend: extract conn reuse from connect_server()
    - MINOR: backend: remove stream usage on connection reuse
    - MINOR: check define check-reuse-pool server keyword
    - MEDIUM: check: implement check-reuse-pool
    - BUILD: backend: silence a build warning when not using ssl
    - BUILD: quic_sock: address a strict-aliasing build warning with gcc 5 and 6
    - BUILD: ssl_ckch: use my_strndup() instead of strndup()
    - DOC: update INSTALL to reflect the minimum compiler version
2025-04-02 18:12:34 +02:00
Willy Tarreau
1450b44bb9 DOC: update INSTALL to reflect the minimum compiler version
The mt_list update in 3.1 mandated the support for c11-like atomics that
arrived with gcc-4.7. As such, older versions are no longer supported.
For special cases in single-threaded environments, mt_lists could be
replaced with regular lists but it doesn't seem worth the hassle. It
was verified that gcc 4.7 to 14 and clang 3.0 and 19 do build fine.
That leaves us with 10 years of coverage of compiler versions, which
remains reasonable assuming that users of old ultra-stable systems are
unlikely to upgrade haproxy without touching the rest of the system.

This should be backported to 3.1.
2025-04-02 18:09:47 +02:00
Willy Tarreau
90e9b9d477 BUILD: ssl_ckch: use my_strndup() instead of strndup()
Not all systems have strndup(), that's why we have our "my_strndup()",
so let's make use of it here. This fixes the build on Solaris 10.
No backport is needed, this was just merged with commit fdcb97614c
("MINOR: ssl/ckch: add substring parser for ckch_conf").
2025-04-02 17:20:03 +02:00
Willy Tarreau
dd900aead8 BUILD: quic_sock: address a strict-aliasing build warning with gcc 5 and 6
The UDP GSO code emits a build warning with older toolchains (gcc 5 and 6):

  src/quic_sock.c: In function 'cmsg_set_gso':
  src/quic_sock.c:683:2: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
    *((uint16_t *)CMSG_DATA(c)) = gso_size;
    ^

Let's just use the write_u16() function that's made for this purpose.
It was verified that for all versions from 5 to 13, gcc produces the
exact same code with the fix (and without the warning). It arrived in
3.1 with commit 448d3d388a ("MINOR: quic: add GSO parameter on quic_sock
send API") so this can be backported there.
2025-04-02 16:07:31 +02:00
Willy Tarreau
870f7aa5cf BUILD: backend: silence a build warning when not using ssl
Since recent commit ee94a6cfc1 ("MINOR: backend: extract conn reuse
from connect_server()") a build warning "set but not used" on the
"reuse" variable is emitted, because indeed the variable is now only
checked when SSL is in use. Let's just mark it as such.
2025-04-02 15:26:31 +02:00
Amaury Denoyelle
f1fb396d71 MEDIUM: check: implement check-reuse-pool
Implement the possibility to reuse idle connections when performing
server checks. This is done thanks to the recently introduced functions
be_calculate_conn_hash() and be_reuse_connection().

One side effect of this change is that be_calculate_conn_hash() can now
be called with a NULL stream instance. As such, part of the functions
are adjusted accordingly.

Note that to simplify configuration, connection reuse is not performed
if any specific check connection parameters are defined on the server
line or via the tcp-check connect rule. This is performed via newly
defined tcpcheck_use_nondefault_connect().
2025-04-02 14:57:40 +02:00
Amaury Denoyelle
e34f748e3a MINOR: check define check-reuse-pool server keyword
Define a new server keyword check-reuse-pool, and its counterpart with a
"no" prefix. For the moment, only parsing is implemented. The real
behavior adjustment will be implemented in the next patch.
2025-04-02 14:57:40 +02:00
Amaury Denoyelle
20eb57b486 MINOR: backend: remove stream usage on connection reuse
Adjust newly defined be_reuse_connection() API. The stream argument is
removed. This will allows checks to be able to invoke it without relying
on a stream instance.
2025-04-02 14:57:40 +02:00
Amaury Denoyelle
ee94a6cfc1 MINOR: backend: extract conn reuse from connect_server()
Following the previous patch, the part directly related to connection
reuse is extracted from connect_server(). It is now define in a new
function be_reuse_connection().
2025-04-02 14:57:40 +02:00
Amaury Denoyelle
c7cc6b6401 MINOR: backend: extract conn hash calculation from connect_server()
On connection reuse, a hash is first calculated. It is generated from
various connection parameters, to retrieve a matching connection.

Extract hash calculation from connect_server() into a new dedicated
function be_calculate_conn_hash(). The objective is to be able to
perform connection reuse for checks, without connect_server() invokation
which relies on a stream instance.
2025-04-02 14:57:40 +02:00
Amaury Denoyelle
4f0240f9a4 MINOR: backend: adjust conn_backend_get() API
The main objective of this patch is to remove the stream instance from
conn_backend_get() parameters. This would allow to perform reuse outside
of stream contexts, for example for checks purpose.
2025-04-02 14:57:40 +02:00
Amaury Denoyelle
2ca616b4e1 MINOR: backend: fix comment when killing idle conns
Previously, if a server reached its pool-high-count limit, connection
were killed on connect_server() when reuse was not possible. However,
this is now performed even if reuse is done since the following patch :
  b3397367dc7cec9e78c62c54efc24d9db5cde2d2
  MEDIUM: connections: Kill connections even if we are reusing one.

Thus, adjust the related comment to reflect this state.
2025-04-02 14:57:40 +02:00
Amaury Denoyelle
2f36162ee1 REGTESTS: extend conn reuse test with transparent proxy
Recently, work on connection reuses reveals an issue when mixed with
transparent proxy and set-dst. This patch rewrites the related regtests
to be able to catch this now fixed bug.

Note that it is the first regtest which relies on bc_reused recently
introduced sample fetches. This fetch could be reuse in other related
connection reuse regtests to simplify them.
2025-04-02 14:57:40 +02:00
Amaury Denoyelle
ec76d52cea MINOR: sample: define bc_reused fetch
Define a new layer4 sample fetch "bc_reused". It is used as a boolean,
set to true if backend connection was reused for the request.
2025-04-02 14:57:40 +02:00
Amaury Denoyelle
5fda64e87e BUG/MEDIUM: backend: fix reuse with set-dst/set-dst-port
On backend connection reuse, a hash is calculated from various
parameters, to ensure the selected connection match the requested
parameters. Notably, destination address is one of these parameters.
However, it is only taken into account if using a transparent server
(server address 0.0.0.0).

This may cause issue where an incorrect connection is reused, which is
not targetted to the correct destination address. This may be the case
if a set-dst/set-dst-port is used with a transparent proxy (proxy option
transparent).

The fix is simple enough. Destination address is now always used as
input to the connection reuse hash.

This must be backported up to 2.6. Note that for reverse HTTP to work,
it relies on the following patch, which ensures destination address
remains NULL in this case.

  commit e94baf6ca71cb2319610baa74dbf17b9bc602b18
  BUG/MINOR: rhttp: fix incorrect dst/dst_port values
2025-04-02 14:57:40 +02:00
Amaury Denoyelle
d7fa8e88c4 BUG/MINOR: backend: do not overwrite srv dst address on reuse
Previously, destination address of backend connection was systematically
always reassigned. However, this step is unnecessary on connection
reuse. Indeed, reuse should only be conducted with connection using the
same destination address matching the stream requirements.

This patch removes this unnecessary assignment. It is now only performed
when reuse cannot be conducted and a new connection is instantiated.

Functionnally speaking, this patch should not change anything in theory,
as reuse is performed in conformance with the destination address.
However, it appears that it was not always properly enforced. The
systematic assignment of the destination address hides these issues, so
it is now remove. The identified bogus cases will then be fixed in the
following patches.would

This should be backported up to all stable versions.
2025-04-02 14:57:40 +02:00
Amaury Denoyelle
c05bb8c967 BUG/MINOR: rhttp: fix incorrect dst/dst_port values
With a @rhttp server, connect is not possible, transfer is only possible
via idle connection reuse. The server does not have any network address.

Thus, it is unnecessary to allocate the stream destination address prior
to connection reuse. This patch adjusts this by fixing
alloc_dst_address() to take this into account.

Prior to this patch, alloc_dst_address() would incorrectly assimilate a
@rhttp server with a transparent proxy mode. Thus stream destination
address would be copied from the destination address. Connection adress
would then be rewrote with this incorrect value. This did not impact
connect or reuse as destination addr is only used in idle conn hash
calculation for transparent servers. However, it causes incorrect values
for dst/dst_port samples.

This should be backported up to 2.9.
2025-04-02 14:57:40 +02:00
Olivier Houchard
f59297e492 BUG/MEDIUM: leastconn: Don't try to reposition if the server is down
It may happen that the server is going down, and fwlc_srv_reposition()
is still called, because streams still attached to the server are
being terminated.
So in fwlc_srv_reposition(), just do nothing if we've been removed from
the tree.

This should fix github issue #2919.

This should not be backported, unless commit
9fe72bba3cf3484577fa1ef00723de08df757996 is also backported.

2025-04-02 12:24:04 +02:00
Willy Tarreau
4ec5509541 BUILD: compiler: undefine the CONCAT() macro if already defined
As Ilya reported in issue #2911, the CONCAT() macro breaks on NetBSD
which defines its own as __CONCAT() (which is exactly the same). Let's
just undefine it before ours to fix the issue instead of renaming, but
keep ours so that we don't have doubts about what we're running with.

Note that the patch introducing this breaking change was backported
to 3.0.
2025-04-02 11:36:43 +02:00
David Carlier
a703eeaef7 MINOR: cpu-topo: cpu_dump_topology() SMT info check little optimisation
Once we stumble across the first cpu having the criteria, we exit
earlier from the loop.
2025-04-02 11:31:37 +02:00
Willy Tarreau
3de99a0919 DOC: config: fix two missing "content" in "tcp-request" examples
As reported by Uku Srmus in GitHub issue #2917, two "tcp-request" rules
in an example were mistakenly missing the "content" hook, rendering them
invalid.

This can be backported.
2025-04-02 11:17:05 +02:00
Ilia Shipitsin
78b849b839 CLEANUP: assorted typo fixes in the code and comments
code, comments and doc actually.
2025-04-02 11:12:20 +02:00
Aurelien DARRAGON
423cca64b6 MINOR: log: support "raw" logformat node typecast
"raw" logformat node typecast is a special value (unlike str,bool,int..)
which tells haproxy to completely ignore logformat options (including
encoding ones) and force binary output for the current node only. It is
mainly intended for use with JSON or CBOR encoders in order to generate
nested CBOR or nested JSON by storing intermediate log-formats within
variables and assembling the final object in the parent log-format.

Example:

  http-request set-var-fmt(txn.intermediate) "%{+json}o %(lower)[str(value)]"

  log-format "%{+json}o %(upper)[str(value)] %(intermediate:raw)[var(txn.intermediate)]"

Would produce:

   {"upper": "value", "intermediate": {"lower": "value"}}
2025-04-02 11:04:43 +02:00
William Lallemand
31bd3627cd BUILD: ssl/ckch: potential null pointer dereference
src/ssl_ckch.c: In function ‘ckch_conf_parse’:
src/ssl_ckch.c:4852:40: error: potential null pointer dereference [-Werror=null-dereference]
 4852 |                                 while (*r) {
      |                                        ^~

Add a test on r before using *r.

No backport needed
2025-04-02 10:02:07 +02:00
William Lallemand
2e8acf54d4 BUG/MINOR: ssl/ckch: leak in error path
fdcb97614cb ("MINOR: ssl/ckch: add substring parser for ckch_conf")
introduced a leak in the error path when the strndup fails.

This patch fixes issue #2920. No backport needed.
2025-04-02 09:53:48 +02:00
Olivier Houchard
9fe72bba3c MAJOR: leastconn; Revamp the way servers are ordered.
For leastconn, servers used to just be stored in an ebtree.
Each server would be one node.
Change that so that nodes contain multiple mt_lists. Each list
will contain servers that share the same key (typically meaning
they have the same number of connections). Using mt_lists means
that as long as tree elements already exist, moving a server from
one tree element to another does no longer require the lbprm write
lock.
We use multiple mt_lists to reduce the contention when moving
a server from one tree element to another. A list in the new
element will be chosen randomly.
We no longer remove a tree element as soon as they no longer
contain any server. Instead, we keep a list of all elements,
and when we need a new element, we look at that list only if it
contains a number of elements already, otherwise we'll allocate
a new one. Keeping nodes in the tree ensures that we very
rarely have to take the lbrpm write lock (as it only happens
when we're moving the server to a position for which no
element is currently in the tree).

The number of mt_lists used is defined as FWLC_NB_LISTS.
The number of tree elements we want to keep is defined as
FWLC_MIN_FREE_ENTRIES, both in defaults.h.
The value used were picked afrer experimentation, and
seems to be the best choice of performances vs memory
usage.

Doing that gives a good boost in performances when a lot of
servers are used.
With a configuration using 500 servers, before that patch,
about 830000 requests per second could be processed, with
that patch, about 1550000 requests per second are
processed, on an 64-cores AMD, using 1200 concurrent connections.
2025-04-01 18:05:30 +02:00
Olivier Houchard
ba521a1d88 MINOR: threads: Add HA_RWLOCK_TRYRDTOWR()
Add HA_RWLOCK_TRYRDTOWR(), that tries to upgrade a lock
from reader to writer, and fails if any seeker or writer already
holds it.
2025-04-01 18:05:30 +02:00
Olivier Houchard
2a9436f96b MINOR: lbprm: Add method to deinit server and proxy
Add two new methods to lbprm, server_deinit() and proxy_deinit(),
in case something should be done at the lbprm level when
removing servers and proxies.
2025-04-01 18:05:30 +02:00
Olivier Houchard
17059098e7 MINOR: mt_list: Implement mt_list_try_lock_prev().
Implement mt_list_try_lock_prev(), that does the same thing
as mt_list_lock_prev(), exceot if the list is locked, it
returns { NULL, NULL } instaed of waiting.
2025-04-01 18:05:30 +02:00
William Lallemand
fdcb97614c MINOR: ssl/ckch: add substring parser for ckch_conf
Add a substring parser for the ckch_conf keyword parser, this will split
a string into multiple substring, and strdup them in a array.
2025-04-01 15:38:32 +02:00
William Lallemand
fa01c9d92b TESTS: jws: change the jwk format
The format of the jwk output changed a little bit because of the
previous commit.
2025-04-01 14:37:22 +02:00
William Lallemand
f8fe84caca MINOR: jws: emit the JWK thumbprint
jwk_thumbprint() is a function which is a function which implements
RFC7368 and emits a JWK thumbprint using a EVP_PKEY.

EVP_PKEY_EC_to_pub_jwk() and EVP_PKEY_RSA_to_pub_jwk() were changed in
order to match what is required to emit a thumbprint (ie, no spaces or
lines and the lexicographic order of the fields)
2025-04-01 11:57:55 +02:00
Willy Tarreau
ed1d4807da EXAMPLES: add "games.cfg" and an example game in Lua
The purpose is mainly to exhibit certain limitations that come with such
less common programming models, to show users how to program interactive
tools in Lua, and how to connect interactively.

Other use cases that could be envisioned are "top" and various monitoring
utilities, with sliding graphs etc. Lua is particularly attractive for
this usage, easy to program, well known from most AI tools (including its
integration into haproxy), making such programs very quick to obtain in
their basic form, and to improve later.

A very limited example game is provided, following the principle of a
very popular one, where the player must compose lines from falling
pieces. It quickly revealed the need to the ability to enforce a timeout
to applet:receive(). Other identified limitations include the difficulty
from the Lua side to monitor multiple events at once, but it seems that
callbacks and/or event dispatchers would be useful here.

At the moment the CLI is not workable (it interactivity was broken in 2.9
when line buffering was adopted), though it was verified that it works
with older releases.

The command needed to connect to the game is displayed as a notice message
during boot.
2025-04-01 09:10:00 +02:00
Willy Tarreau
2c779f3938 BUG/MINOR: config: silence .notice/.warning/.alert in discovery mode
When first pre-parsing the config to detect the presence or absence of
the master mode, we must not emit messages because they are not supposed
to be visible at this point, otherwise they appear twice each. The
pre-parsing, also called discovery mode, is only for internal use,
thus it should remain silent.

This should be backported to 3.1 where this mode was introduced.
2025-04-01 09:06:25 +02:00
Willy Tarreau
9f00702dc6 MINOR: cpu-topo: add new cpu-policies "group-by-2-clusters" and above
This adds "group-by-{2,3,4}-clusters", which, as its name implies,
create one thread group per X clusters. This can be useful when CPUs
are split into too small clusters, as well as when the total number
of assigned cores is not even between the clusters, to try to spread
the load between less different ones.
2025-03-31 16:21:37 +02:00
Willy Tarreau
1e9a2529aa MINOR: cpu-topo: pass an extra argument to ha_cpu_policy
This extra argument will allow common functions to distinguish between
multiple policies. For now it's not used.
2025-03-31 16:21:37 +02:00
Willy Tarreau
e4053b0d09 MINOR: cpu-topo: add a dump of thread-to-CPU mapping to -dc
When emitting the CPU topology info with -dc, also emit a list of
thread-to-CPU mapping. The group/thread and thread ID are emitted
with the list of their CPUs on each line. The count of CPUs is shown
to ease comparisons, and as much as possible, we try to pack identical
lines within a group by showing thread ranges.
2025-03-31 16:21:37 +02:00
Willy Tarreau
571573874a MINOR: cpu-set: add a new function to print cpu-sets in human-friendly mode
The new function "print_cpu_set()" will print cpu sets in a human-friendly
way, with commas and dashes for intervals. The goal is to keep them compact
enough.
2025-03-31 16:21:37 +02:00
Willy Tarreau
3955f151b1 MINOR: cpu-set: compare two cpu sets with ha_cpuset_isequal()
This function returns true if two CPU sets are equal.
2025-03-31 16:21:37 +02:00
Willy Tarreau
e17512c3b2 MINOR: thread: dump the CPU topology in thread_map_to_groups()
It was previously done in thread_detect_count() but that's not quite
handy because we still don't know about the groups setting. Better do
it slightly later and have all the relevant info instead.
2025-03-31 15:42:13 +02:00
Valentine Krasnobaeva
b303861469 MINOR: compiler: add __nonstring macro
GCC 15 throws the following warning on fixed-size char arrays if they do not
contain terminated NUL:

src/tools.c:2041:25: error: initializer-string for array of 'char' truncates NUL terminator but destination lacks 'nonstring' attribute (17 chars into 16 available) [-Werror=unterminated-string-initialization]
 2041 | const char hextab[16] = "0123456789ABCDEF";

We are using a couple of such definitions for some constants. Converting them
to flexible arrays, like: hextab[] = "0123456789ABCDEF" may have consequences,
as enlarged arrays won't fit anymore where they were possibly located due to
the memory alignement constraints.

GCC adds 'nonstring' variable attribute for such char arrays, but clang and
other compilers don't have it. Let's wrap 'nonstring' with our
__nonstring macro, which will test if the compiler supports this attribute.

This fixes the issue #2910.
2025-03-31 13:50:28 +02:00
Ilia Shipitsin
415d446065 CI: QUIC Interop on LibreSSL: allow "on: workflow_dispatch" in forks
previously that build were limited to "haproxy" github organization
only. let's allow manual builds from forks
2025-03-28 09:51:35 +01:00
Ilia Shipitsin
8d591c387a CI: QUIC Interop on AWS-LC: allow "on: workflow_dispatch" in forks
previously that build were limited to "haproxy" github organization
only. let's allow manual builds from forks
2025-03-28 09:51:35 +01:00
Ilia Shipitsin
7de45e3874 CI: NetBSD: allow "on: workflow_dispatch" in forks
previously that build were limited to "haproxy" github organization
only. let's allow manual builds from forks
2025-03-28 09:51:35 +01:00
Ilia Shipitsin
8231f58fdc CI: Illumos: allow "on: workflow_dispatch" in forks
previously that build were limited to "haproxy" github organization
only. let's allow manual builds from forks
2025-03-28 09:51:35 +01:00
Ilia Shipitsin
7495dbed22 CI: cross compile: allow "on: workflow_dispatch" in forks
previously that build were limited to "haproxy" github organization
only. let's allow manual builds from forks
2025-03-28 09:51:35 +01:00
Ilia Shipitsin
7eb54656ae CI: coverity scan: allow "on: workflow_dispatch" in forks
previously that build were limited to "haproxy" github organization
only. let's allow manual builds from forks
2025-03-28 09:51:35 +01:00
Ilia Shipitsin
424ca19831 CI: spellcheck: allow "on: workflow_dispatch" in forks
previously that build were limited to "haproxy" github organization
only. let's allow manual builds from forks
2025-03-28 09:51:35 +01:00
Ilia Shipitsin
d9cb95c2a5 CI: fedora rawhide: install "awk" as a dependency
for some reason it is not installed by default on rawhide anymore
2025-03-28 09:51:35 +01:00
Ilia Shipitsin
21894300c1 CI: fedora rawhide: allow "on: workflow_dispatch" in forks
previously that build were limited to "haproxy" github organization
only. let's allow manual builds from forks
2025-03-28 09:51:35 +01:00
Valentine Krasnobaeva
44f98f1747 BUG/MINOR: log: fix gcc warn about truncating NUL terminator while init char arrays
gcc 15 throws such kind of warnings about initialization of some char arrays:

src/log.c:181:33: error: initializer-string for array of 'char' truncates NUL terminator but destination lacks 'nonstring' attribute (17 chars into 16 available) [-Werror=unterminated-string-initialization]
  181 | const char sess_term_cond[16] = "-LcCsSPRIDKUIIII"; /* normal, Local, CliTo, CliErr, SrvTo, SrvErr, PxErr, Resource, Internal, Down, Killed, Up, -- */
      |                                 ^~~~~~~~~~~~~~~~~~
src/log.c:182:33: error: initializer-string for array of 'char' truncates NUL terminator but destination lacks 'nonstring' attribute (9 chars into 8 available) [-Werror=unterminated-string-initialization]
  182 | const char sess_fin_state[8]  = "-RCHDLQT";     /* cliRequest, srvConnect, srvHeader, Data, Last, Queue, Tarpit */

So, let's make it happy by not giving the sizes of these char arrays
explicitly, thus he can accomodate there NUL terminators.

Reported in GitHub issue #2910.

This should be backported up to 2.6.
2025-03-27 11:52:33 +01:00
Willy Tarreau
9b53a4a7fb REGTESTS: disable the test balance/balance-hash-maxqueue
This test brought by commit 8ed1e91efd ("MEDIUM: lb-chash: add directive
hash-preserve-affinity") seems to have hit a limitation of what can be
expressed in vtc, as it would be desirable to have one server response
release two clients at once but the various attempts using barriers
have failed so far. The test seems to work fine locally but still fails
almost 100% of the time on the CI, so it remains timing dependent in
some ways. Tests have been done with nbthread 1, pool-idle-shared off,
http-reuse never (since always fails locally) etc but to no avail. Let's
just mark it broken in case we later figure another way to fix it. It's
still usable locally most of the time, though.
2025-03-25 18:24:49 +01:00
Willy Tarreau
6b17310757 MEDIUM: pools: be a bit smarter when merging comparable size pools
By default, pools of comparable sizes are merged together. However, the
current algorithm is dumb: it rounds the requested size to the next
multiple of 16 and compares the sizes like this. This results in many
entries which are already multiples of 16 not being merged, for example
1024 and 1032 are separate, 65536 and 65540 are separate, 48 and 56 are
separate (though 56 merges with 64).

This commit changes this to consider not just the entry size but also the
average entry size, that is, it compares the average size of all objects
sharing the pool with the size of the object looking for a pool. If the
object is not more than 1% bigger nor smaller than the current average
size or if it neither 16 bytes smaller nor larger, then it can be merged.
Also, it always respects exact matches in order to avoid merging objects
into larger pools or worse, extending existing ones for no reason, and
when there's a tie, it always avoids extending an existing pool.

Also, we now visit all existing pools in order to spot the best one, we
do not stop anymore at the smallest one large enough. Theoretically this
could cost a bit of CPU but in practice it's O(N^2) with N quite small
(typically in the order of 100) and the cost at each step is very low
(compare a few integer values). But as a side effect, pools are no
longer sorted by size, "show pools bysize" is needed for this.

This causes the objects to be much better grouped together, accepting to
use a little bit more sometimes to avoid fragmentation, without causing
everyone to be merged into the same pool. Thanks to this we're now
seeing 36 pools instead of 48 by default, with some very nice examples
of compact grouping:

  - Pool qc_stream_r (80 bytes) : 13 users
      >  qc_stream_r : size=72 flags=0x1 align=0
      >  quic_cstrea : size=80 flags=0x1 align=0
      >  qc_stream_a : size=64 flags=0x1 align=0
      >  hlua_esub   : size=64 flags=0x1 align=0
      >  stconn      : size=80 flags=0x1 align=0
      >  dns_query   : size=64 flags=0x1 align=0
      >  vars        : size=80 flags=0x1 align=0
      >  filter      : size=64 flags=0x1 align=0
      >  session pri : size=64 flags=0x1 align=0
      >  fcgi_hdr_ru : size=72 flags=0x1 align=0
      >  fcgi_param_ : size=72 flags=0x1 align=0
      >  pendconn    : size=80 flags=0x1 align=0
      >  capture     : size=64 flags=0x1 align=0

  - Pool h3s (56 bytes) : 17 users
      >  h3s         : size=56 flags=0x1 align=0
      >  qf_crypto   : size=48 flags=0x1 align=0
      >  quic_tls_se : size=48 flags=0x1 align=0
      >  quic_arng   : size=56 flags=0x1 align=0
      >  hlua_flt_ct : size=56 flags=0x1 align=0
      >  promex_metr : size=48 flags=0x1 align=0
      >  conn_hash_n : size=56 flags=0x1 align=0
      >  resolv_requ : size=48 flags=0x1 align=0
      >  mux_pt      : size=40 flags=0x1 align=0
      >  comp_state  : size=40 flags=0x1 align=0
      >  notificatio : size=48 flags=0x1 align=0
      >  tasklet     : size=56 flags=0x1 align=0
      >  bwlim_state : size=48 flags=0x1 align=0
      >  xprt_handsh : size=48 flags=0x1 align=0
      >  email_alert : size=56 flags=0x1 align=0
      >  caphdr      : size=41 flags=0x1 align=0
      >  caphdr      : size=41 flags=0x1 align=0

  - Pool quic_cids (32 bytes) : 13 users
      >  quic_cids   : size=16 flags=0x1 align=0
      >  quic_tls_ke : size=32 flags=0x1 align=0
      >  quic_tls_iv : size=12 flags=0x1 align=0
      >  cbuf        : size=32 flags=0x1 align=0
      >  hlua_queuew : size=24 flags=0x1 align=0
      >  hlua_queue  : size=24 flags=0x1 align=0
      >  promex_modu : size=24 flags=0x1 align=0
      >  cache_st    : size=24 flags=0x1 align=0
      >  spoe_appctx : size=32 flags=0x1 align=0
      >  ehdl_sub_tc : size=32 flags=0x1 align=0
      >  fcgi_flt_ct : size=16 flags=0x1 align=0
      >  sig_handler : size=32 flags=0x1 align=0
      >  pipe        : size=24 flags=0x1 align=0

  - Pool quic_crypto (1032 bytes) : 2 users
      >  quic_crypto : size=1032 flags=0x1 align=0
      >  requri      : size=1024 flags=0x1 align=0

  - Pool quic_conn_r (65544 bytes) : 2 users
      >  quic_conn_r : size=65536 flags=0x1 align=0
      >  dns_msg_buf : size=65540 flags=0x1 align=0

On a very unscientific test consisting in sending 1 million H1 requests
and 1 million H2 requests to the stats page, we're seeing an ~6% lower
memory usage with the patch:

  before the patch:
    Total: 48 pools, 4120832 bytes allocated, 4120832 used (~3555680 by thread caches).

  after the patch:
    Total: 36 pools, 3880648 bytes allocated, 3880648 used (~3299064 by thread caches).

This should be taken with care however since pools allocate and release
in batches.
2025-03-25 18:01:01 +01:00
Pierre-Andre Savalle
8ed1e91efd MEDIUM: lb-chash: add directive hash-preserve-affinity
When using hash-based load balancing, requests are always assigned to
the server corresponding to the hash bucket for the balancing key,
without taking maxconn or maxqueue into account, unlike in other load
balancing methods like 'first'. This adds a new backend directive that
can be used to take maxconn and possibly maxqueue in that context. This
can be used when hashing is desired to achieve cache locality, but
sending requests to a different server is preferable to queuing for a
long time or failing requests when the initial server is saturated.

By default, affinity is preserved as was the case previously. When
'hash-preserve-affinity' is set to 'maxqueue', servers are considered
successively in the order of the hash ring until a server that does not
have a full queue is found.

When 'maxconn' is set on a server, queueing cannot be disabled, as
'maxqueue=0' means unlimited.  To support picking a different server
when a server is at 'maxconn' irrespective of the queue,
'hash-preserve-affinity' can be set to 'maxconn'.
2025-03-25 18:01:01 +01:00
Amaury Denoyelle
cf9e40bd8a MINOR: quic: define max-stream-data configuration as a ratio 2025-03-25 16:30:35 +01:00
Amaury Denoyelle
68c10d444d MINOR: mux-quic: define config for max-data
Define a new global configuration tune.quic.frontend.max-data. This
allows users to explicitely set the value for the corresponding QUIC TP
initial-max-data, with direct impact on haproxy memory consumption.
2025-03-25 16:30:09 +01:00
Amaury Denoyelle
1f1a18e318 MINOR: quic: ignore uni-stream for initial max data TP
Initial TP value for max-data is automatically calculated to be adjusted
to the maximum number of opened streams over a QUIC connection. This
took into account both max-streams-bidi-remote and uni-streams. By
default, this is equivalent to 100 + 3 = 103 max opened streams.

This patch simplifies the calculation by only using bidirectional
streams. Uni streams are ignored because they are only used for HTTP/3
control exchanges, which should only represents a few bytes. For now,
users can only configure the max number of remote bidi streams, so the
simplified calculation should make more sense to them.

Note that this relies on the assumption that HTTP/3 is used as
application protocol. To support other protocols, it may be necessary to
review this and take into account both local bidi and uni streams.
2025-03-25 16:29:38 +01:00
Amaury Denoyelle
3db5320289 CLEANUP: quic: reorganize TP flow-control initialization
Adjust initialization of flow-control transport parameters via
quic_transport_params_init().

This is purely cosmetic, with some comments added. It is also a
preparatory step for future patches with addition of new configuration
keywords related to flow-control TP values.
2025-03-25 16:29:35 +01:00
Amaury Denoyelle
a71007c088 MINOR: quic: move global tune options into quic_tune
A new structure quic_tune has recently been defined. Its purpose is to
store global options related to QUIC. Previously, only the tunable to
toggle pacing was stored in it.

This commit moves several QUIC related tunable from global to quic_tune
structure. This better centralizes QUIC configuration option and gives
room for future generic options.
2025-03-24 10:01:46 +01:00
Willy Tarreau
119a79f479 [RELEASE] Released version 3.2-dev8
Released version 3.2-dev8 with the following main changes :
    - MINOR: jws: implement JWS signing
    - TESTS: jws: implement a test for JWS signing
    - CI: github: add "jose" to apt dependencies
    - CLEANUP: log-forward: remove useless options2 init
    - CLEANUP: log: add syslog_process_message() helper
    - MINOR: proxy: add proxy->options3
    - MINOR: log: migrate log-forward options from proxy->options2 to options3
    - MINOR: log: provide source address information in syslog_process_message()
    - MINOR: tools: only print address in sa2str() when port == -1
    - MINOR: log: add "option host" log-forward option
    - MINOR: log: handle log-forward "option host"
    - MEDIUM: log: change default "host" strategy for log-forward section
    - BUG/MEDIUM: thread: use pthread_self() not ha_pthread[tid] in set_affinity
    - MINOR: compiler: add a simple macro to concatenate resolved strings
    - MINOR: compiler: add a new __decl_thread_var() macro to declare local variables
    - BUILD: tools: silence a build warning when USE_THREAD=0
    - BUILD: backend: silence a build warning when threads are disabled
    - DOC: management: rename some last occurences from domain "dns" to "resolvers"
    - BUG/MINOR: stats: fix capabilities and hide settings for some generic metrics
    - MINOR: cli: export cli_io_handler() to ease symbol resolution
    - MINOR: tools: improve symbol resolution without dl_addr
    - MINOR: tools: ease the declaration of known symbols in resolve_sym_name()
    - MINOR: tools: teach resolve_sym_name() a few more common symbols
    - BUILD: tools: avoid a build warning on gcc-4.8 in resolve_sym_name()
    - DEV: ncpu: also emulate sysconf() for _SC_NPROCESSORS_*
    - DOC: design-thoughts: commit numa-auto.txt
    - MINOR: cpuset: make the API support negative CPU IDs
    - MINOR: thread: rely on the cpuset functions to count bound CPUs
    - MINOR: cpu-topo: add ha_cpu_topo definition
    - MINOR: cpu-topo: allocate and initialize the ha_cpu_topo array.
    - MINOR: cpu-topo: rely on _SC_NPROCESSORS_CONF to trim maxcpus
    - MINOR: cpu-topo: add a function to dump CPU topology
    - MINOR: cpu-topo: update CPU topology from excluded CPUs at boot
    - REORG: cpu-topo: move bound cpu detection from cpuset to cpu-topo
    - MINOR: cpu-topo: add detection of online CPUs on Linux
    - MINOR: cpu-topo: add detection of online CPUs on FreeBSD
    - MINOR: cpu-topo: try to detect offline cpus at boot
    - MINOR: cpu-topo: add CPU topology detection for linux
    - MINOR: cpu-topo: also store the sibling ID with SMT
    - MINOR: cpu-topo: add NUMA node identification to CPUs on Linux
    - MINOR: cpu-topo: add NUMA node identification to CPUs on FreeBSD
    - MINOR: thread: turn thread_cpu_mask_forced() into an init-time variable
    - MINOR: cfgparse: move the binding detection into numa_detect_topology()
    - MINOR: cfgparse: use already known offline CPU information
    - MINOR: global: add a command-line option to enable CPU binding debugging
    - MINOR: cpu-topo: add a new "cpu-set" global directive to choose cpus
    - MINOR: cpu-topo: add "drop-cpu" and "only-cpu" to cpu-set
    - MEDIUM: thread: start to detect thread groups and threads min/max
    - MEDIUM: cpu-topo: make sure to properly assign CPUs to threads as a fallback
    - MEDIUM: thread: reimplement first numa node detection
    - MEDIUM: cfgparse: remove now unused numa & thread-count detection
    - MINOR: cpu-topo: refine cpu dump output to better show kept/dropped CPUs
    - MINOR: cpu-topo: fall back to nominal_perf and scaling_max_freq for the capacity
    - MINOR: cpu-topo: use cpufreq before acpi cppc
    - MINOR: cpu-topo: boost the capacity of performance cores with cpufreq
    - MINOR: cpu-topo: skip CPU detection when /sys/.../cpu does not exist
    - MINOR: cpu-topo: skip identification of non-existing CPUs
    - MINOR: cpu-topo: skip CPU properties that we've verified do not exist
    - MINOR: cpu-topo: implement a sorting mechanism for CPU index
    - MINOR: cpu-topo: implement a sorting mechanism by CPU locality
    - MINOR: cpu-topo: implement a CPU sorting mechanism by cluster ID
    - MINOR: cpu-topo: ignore single-core clusters
    - MINOR: cpu-topo: assign clusters to cores without and renumber them
    - MINOR: cpu-topo: make sure we don't leave unassigned IDs in the cpu_topo
    - MINOR: cpu-topo: assign an L3 cache if more than 2 L2 instances
    - MINOR: cpu-topo: renumber cores to avoid holes and make them contiguous
    - MINOR: cpu-topo: add a function to sort by cluster+capacity
    - MINOR: cpu-topo: consider capacity when forming clusters
    - MINOR: cpu-topo: create an array of the clusters
    - MINOR: cpu-topo: ignore excess of too small clusters
    - MINOR: cpu-topo: add "only-node" and "drop-node" to cpu-set
    - MINOR: cpu-topo: add "only-thread" and "drop-thread" to cpu-set
    - MINOR: cpu-topo: add "only-core" and "drop-core" to cpu-set
    - MINOR: cpu-topo: add "only-cluster" and "drop-cluster" to cpu-set
    - MINOR: cpu-topo: add a CPU policy setting to the global section
    - MINOR: cpu-topo: add a 'first-usable-node' cpu policy
    - MEDIUM: cpu-topo: use the "first-usable-node" cpu-policy by default
    - CLEANUP: thread: now remove the temporary CPU node binding code
    - MINOR: cpu-topo: add cpu-policy "group-by-cluster"
    - MEDIUM: cpu-topo: let the "group-by-cluster" split groups
    - MINOR: cpu-topo: add a new "performance" cpu-policy
    - MINOR: cpu-topo: add a new "efficiency" cpu-policy
    - MINOR: cpu-topo: add a new "resource" cpu-policy
    - MINOR: jws: add new functions in jws.h
    - MINOR: cpu-topo: fix unused stack var 'cpu2' reported by coverity
    - MINOR: hlua: add an optional timeout to AppletTCP:receive()
    - MINOR: jws: use jwt_alg type instead of a char
    - BUG/MINOR: log: prevent saddr NULL deref in syslog_io_handler()
    - MINOR: stream: decrement srv->served after detaching from the list
    - BUG/MINOR: hlua: fix optional timeout argument index for AppletTCP:receive()
    - MINOR: server: simplify srv_has_streams()
    - CLEANUP: server: make it clear that srv_check_for_deletion() is thread-safe
    - MINOR: cli/server: don't take thread isolation to check for srv-removable
    - BUG/MINOR: limits: compute_ideal_maxconn: don't cap remain if fd_hard_limit=0
    - MINOR: limits: fix check_if_maxsock_permitted description
    - BUG/MEDIUM: hlua/cli: fix cli applet UAF in hlua_applet_wakeup()
    - MINOR: tools: path_base() concatenates a path with a base path
    - MEDIUM: ssl/ckch: make the ckch_conf more generic
    - BUG/MINOR: mux-h2: Reset streams with NO_ERROR code if full response was already sent
    - MINOR: stats: add .generic explicit field in stat_col struct
    - MINOR: stats: STATS_PX_CAP___B_ macro
    - MINOR: stats: add .cap for some static metrics
    - MINOR: stats: use stat_col storage stat_cols_info
    - MEDIUM: promex: switch to using stat_cols_info for global metrics
    - MINOR: promex: expose ST_I_INF_WARNINGS (AKA total_warnings) metric
    - MEDIUM: promex: switch to using stat_cols_px for front/back/server metrics
    - MINOR: stats: explicitly add frontend cap for ST_I_PX_REQ_TOT
    - CLEANUP: promex: remove unused PROMEX_FL_{INFO,FRONT,BACK,LI,SRV} flags
    - BUG/MEDIUM: mux-quic: fix crash on RS/SS emission if already close local
    - BUG/MINOR: mux-quic: remove extra BUG_ON() in _qcc_send_stream()
    - MEDIUM: mt_list: Reduce the max number of loops with exponential backoff
    - MINOR: stats: add alt_name field to stat_col struct
    - MINOR: stats: add alt name info to stat_cols_info where relevant
    - MINOR: promex: get rid of promex_global_metric array
    - MINOR: stats-proxy: add alt_name field for ME_NEW_{FE,BE,PX} helpers
    - MINOR: stats-proxy: add alt name info to stat_cols_px where relevant
    - MINOR: promex: get rid of promex_st_metrics array
    - MINOR: pools: rename the "by_what" field of the show pools context to "how"
    - MINOR: cli/pools: record the list of pool registrations even when merging them
2025-03-21 17:33:36 +01:00
Willy Tarreau
9091c5317f MINOR: cli/pools: record the list of pool registrations even when merging them
By default, create_pool() tries to merge similar pools into one. But when
dealing with certain bugs, it's hard to say which ones were merged together.
We do have the information at registration time, so let's just create a
list of registrations ("pool_registration") attached to each pool, that
will store that information. It can then be consulted on the CLI using
"show pools detailed", where the names, sizes, alignment and flags are
reported.
2025-03-21 17:09:30 +01:00
Willy Tarreau
baf8b742b4 MINOR: pools: rename the "by_what" field of the show pools context to "how"
The goal will be to support other dump options. We don't need 32 bits to
express sorting criteria, let's reserve only 4 bits for them and leave
the remaining ones unused.
2025-03-21 17:09:30 +01:00
Aurelien DARRAGON
83074bf690 MINOR: promex: get rid of promex_st_metrics array
In this patch we pursue the work started in a5aadbd ("MEDIUM: promex:
switch to using stat_cols_px for front/back/server metrics"):

Indeed, while having ".promex_name" info in stat_cols_info generic array
was confusing, Willy suggested that we have ".alt_name" which stays
generic and may be considered by alternative exporters for metric naming.
For now, only promex exporter will make use of it.

Thanks to this, it allows us to completely get rid of the
stat_cols_px array. The other main benefit is that it will be much harder
to overlook promex metric definition now because .alt_name has more
visibility in the main metric array rather than in an addon file.
2025-03-21 17:05:31 +01:00
Aurelien DARRAGON
276491dc22 MINOR: stats-proxy: add alt name info to stat_cols_px where relevant
For all metrics defined under promex_st_metrics array, add the
corresponding .alt_name field in the general purpose stat_cols_px
array.
2025-03-21 17:05:26 +01:00
Aurelien DARRAGON
7f9d8c1327 MINOR: stats-proxy: add alt_name field for ME_NEW_{FE,BE,PX} helpers
For now alt_name is systematically set to NULL. Thanks to this change we
may easily add an altname to existing metrics. Also by requiring explicit
value it offers more visibility for this field.
2025-03-21 17:05:19 +01:00
Aurelien DARRAGON
155fb4ec74 MINOR: promex: get rid of promex_global_metric array
In this patch we pursue the work started in 1adc796 ("MEDIUM: promex:
switch to using stat_cols_info for global metrics"):

Indeed, while having ".promex_name" info in stat_cols_info generic array
was confusing, Willy suggested that we have ".alt_name" which stays
generic and may be considered by alternative exporters for metric naming.
For now, only promex exporter will make use of it.

Thanks to this, it allows us to completely get rid of the
promex_global_metric array. The other main benefit is that it will be
much harder to overlook promex metric definition now because .alt_name
has more visibility in the main metric array rather than in an addon file.
2025-03-21 17:05:14 +01:00
Aurelien DARRAGON
b03e05cd36 MINOR: stats: add alt name info to stat_cols_info where relevant
For all metrics defined under promex_global_metrics array, add the
corresponding .alt_name field in the general purpose stat_cols_info
array.
2025-03-21 17:05:02 +01:00
Aurelien DARRAGON
7ec6f4412c MINOR: stats: add alt_name field to stat_col struct
alt_name will be used by metric exporters to know how the metric should be
presented to the user. If the alt_name is NULL, the metric should be
ignored. For now only promex exporter will make use of this.
2025-03-21 17:04:54 +01:00
Olivier Houchard
98967aa09f MEDIUM: mt_list: Reduce the max number of loops with exponential backoff
Reduce the max number of loops in the mt_list code while waiting for
a lock to be available with exponential backoff. It's been observed that
the current value led to severe performances degradation at least on
some hardware, hopefully this value will be acceptable everywhere.
2025-03-21 11:30:59 +01:00
Amaury Denoyelle
c5f8df8d55 BUG/MINOR: mux-quic: remove extra BUG_ON() in _qcc_send_stream()
The following patch fixed a BUG_ON() which could be triggered if RS/SS
emission was scheduled after stream local closure.
  7ee1279f4b8416435faba5cb93a9be713f52e4df
  BUG/MEDIUM: mux-quic: fix crash on RS/SS emission if already close local

qcc_send_stream() was rewritten as a wrapper around an internal
_qcc_send_stream() used to bypass the faulty BUG_ON(). However, an extra
unnecessary BUG_ON() was added by mistake in _qcc_send_stream().

This should not cause any issue, as the BUG_ON() is only active if <urg>
argument is false, which is not the case for RS/SS emission. However,
this patch is labelled as a bug as this BUG_ON() is unnecessary and may
cause issues in the future.

This should be backported up to 2.8, after the above mentionned patch.
2025-03-20 18:18:52 +01:00
Amaury Denoyelle
7ee1279f4b BUG/MEDIUM: mux-quic: fix crash on RS/SS emission if already close local
A BUG_ON() is present in qcc_send_stream() to ensure that emission is
never performed with a stream already closed locally. However, this
function is also used for RESET_STREAM/STOP_SENDING emission. No
protection exists to ensure that RS/SS is not scheduled after stream
local closure, which would result in this BUG_ON() crash.

This crash can be triggered with the following QUIC client sequence :
1. SS is emitted to open a new stream. QUIC-MUX schedules a RS emission
   by and the stream is locally closed.
2. An invalid HTTP/3 request is sent on the same stream, for example
   with duplicated pseudo-headers. The objective is to ensure
   qcc_abort_stream_read() is called after stream closure, which results
   in the following backtrace.

 0x000055555566a620 in qcc_send_stream (qcs=0x7ffff0061420, urg=1, count=0) at src/mux_quic.c:1633
 1633            BUG_ON(qcs_is_close_local(qcs));
 [ ## gdb ## ] bt
 #0  0x000055555566a620 in qcc_send_stream (qcs=0x7ffff0061420, urg=1, count=0) at src/mux_quic.c:1633
 #1  0x000055555566a921 in qcc_abort_stream_read (qcs=0x7ffff0061420) at src/mux_quic.c:1658
 #2  0x0000555555685426 in h3_rcv_buf (qcs=0x7ffff0061420, b=0x7ffff748d3f0, fin=0) at src/h3.c:1454
 #3  0x0000555555668a67 in qcc_decode_qcs (qcc=0x7ffff0049eb0, qcs=0x7ffff0061420) at src/mux_quic.c:1315
 #4  0x000055555566c76e in qcc_recv (qcc=0x7ffff0049eb0, id=12, len=0, offset=23, fin=0 '\000',
     data=0x7fffe0049c1c "\366\r,\230\205\354\234\301;\2563\335\037k\306\334\037\260", <incomplete sequence \323>) at src/mux_quic.c:1901
 #5  0x0000555555692551 in qc_handle_strm_frm (pkt=0x7fffe00484b0, strm_frm=0x7ffff00539e0, qc=0x7fffe0049220, fin=0 '\000') at src/quic_rx.c:635
 #6  0x0000555555694530 in qc_parse_pkt_frms (qc=0x7fffe0049220, pkt=0x7fffe00484b0, qel=0x7fffe0075fc0) at src/quic_rx.c:980
 #7  0x0000555555696c7a in qc_treat_rx_pkts (qc=0x7fffe0049220) at src/quic_rx.c:1324
 #8  0x00005555556b781b in quic_conn_app_io_cb (t=0x7fffe0037f20, context=0x7fffe0049220, state=49232) at src/quic_conn.c:601
 #9  0x0000555555d53788 in run_tasks_from_lists (budgets=0x7ffff748e2b0) at src/task.c:603
 #10 0x0000555555d541ae in process_runnable_tasks () at src/task.c:886
 #11 0x00005555559c39e9 in run_poll_loop () at src/haproxy.c:2858
 #12 0x00005555559c41ea in run_thread_poll_loop (data=0x55555629fb40 <ha_thread_info+64>) at src/haproxy.c:3075

The proper solution is to not execute this BUG_ON() for RS/SS emission.
Indeed, it is valid and can be useful to emit these frames, even after
stream local closure.

To implement this, qcc_send_stream() has been rewritten as a mere
wrapper function around the new internal _qcc_send_stream(). The latter
is used only by QMUX for STREAM, RS and SS emission. Application layer
continue to use the original function for STREAM emission, with the
BUG_ON() still in place there.

This must be backported up to 2.8.
2025-03-20 17:32:14 +01:00
Aurelien DARRAGON
85f2f93d11 CLEANUP: promex: remove unused PROMEX_FL_{INFO,FRONT,BACK,LI,SRV} flags
Now promex metric dumping relies on stat_cols API, we don't make use of
these flags, so let's remove them.
2025-03-20 11:42:58 +01:00
Aurelien DARRAGON
2ab82124ec MINOR: stats: explicitly add frontend cap for ST_I_PX_REQ_TOT
While being a generic metric, ST_I_PX_REQ_TOT is handled specifically for
the frontend case. But the frontend capability isn't set for that metric
It is actually quite misleading, because the capability may be checked
to see whether the metric is relevant for a given scope, yet it is
relevant for frontend scope.

In this patch we also add the frontend capability for the metric.
2025-03-20 11:42:43 +01:00
Aurelien DARRAGON
a5aadbd512 MEDIUM: promex: switch to using stat_cols_px for front/back/server metrics
Now the stat_cols_px array contains all info that-prometheus requires
stop using the promex_st_metrics array that contains redundant infos.

As for ("MEDIUM: promex: switch to using stat_cols_info for global
metrics"), initial goal was to completely get rid of promex_st_metrics
array, but it turns out it is still required but only for the name
mapping part now. So in this commit we change it from complex structure
array (with redundant info) to a simple ist array with the
metric id:promex name mapping. If a metric name is not defined there, then
promex ignores it.
2025-03-20 11:40:07 +01:00
Aurelien DARRAGON
d31ef6134a MINOR: promex: expose ST_I_INF_WARNINGS (AKA total_warnings) metric
It has been requested to have the ST_I_INF_WARNINGS metric available from
prometheus, let's define it in promex_global_metrics ist array so that
prometheus starts advertising it.
2025-03-20 11:39:16 +01:00
Aurelien DARRAGON
1adc796c4b MEDIUM: promex: switch to using stat_cols_info for global metrics
Now the stat_cols_info array contains all info that prometheus requires,
stop using the promex_global_metrics array that contains redundant infos.

Initial goal was to completely drop the promex_global_metrics array.
However it was deemed no longer relevant as prometheus stats rely on a
custom name that cannot be derived from stat_cols_info[], unless we add
a specific ".promex_name" field or similar to name the stats for
prometheus. This is what was carried over on a first attempt but it proved
to burden stat_cols_info[] array (not only memory wise, it is quite
confusing to see promex in the main codebase, given that prometheus is
shipped as an optional add-on).

The new strategy consists in revamping the promex_global_metrics array
from promex_metric (with all redundant fields for metrics) to a simple
ID<==>IST mapping. If the metric is mapped, then it means promex addon
should advertise it (using the name provided in the mapping). Now for
all the metric retrieval, no longer rely on built-in hardcoded values
but instead leverage the new stat cols API.

The tricky part is the .type association because the general rule doesn't
apply for all metrics as it seems that we stated that some non-counters
oriented metrics (at least from haproxy point of view) had to be presented
as counter metrics. So in this patch we add some special treatment for
those metrics to emulate the old behavior. If that's not relevant in the
future, it may be removed. But this requires to ensure that promex users
will properly cope with that change. At least for now, no change of
behavior should be expected.
2025-03-20 11:38:56 +01:00
Aurelien DARRAGON
af68343a56 MINOR: stats: use stat_col storage stat_cols_info
Use stat_col storage for stat_cols_info[] array instead of name_desc.

As documented in 65624876f ("MINOR: stats: introduce a more expressive
stat definition method"), stat_col supersedes name_desc storage but
it remains backward compatible. Here we migrate to the new API to be
able to further extend stat_cols_info[] in following patches.
2025-03-20 11:38:32 +01:00
Aurelien DARRAGON
8aa8626d12 MINOR: stats: add .cap for some static metrics
Goal is to merge promex metrics definition into the main one.
Promex metrics will use the metric capability to know available scopes,
thus only metrics relevant for prometheus were updated.
2025-03-20 11:38:17 +01:00
Aurelien DARRAGON
9c60fc9fe1 MINOR: stats: STATS_PX_CAP___B_ macro
STATS_PX_CAP___B_ points to STATS_PX_CAP_BE, it is just an alias
for consistency, like STATS_PX_CAP____S which points to
STATS_PX_CAP_SRV.
2025-03-20 11:37:47 +01:00
Aurelien DARRAGON
3c1b00b127 MINOR: stats: add .generic explicit field in stat_col struct
Further extend logic implemented in 65624876 ("MINOR: stats: introduce a
more expressive stat definition method") and 4e9e8418 ("MINOR: stats:
prepare stats-file support for values other than FN_COUNTER"): we don't
rely anymore on the presence of the capability to know if the metric is
generic or not. This is because it prevents us from setting a capability
on static statistics. Yet it could be useful to set the capability even
on static metrics, thus we add a dedicated .generic bit to tell haproxy
that the metric is generic and can be handled automatically by the API.

Also, ME_NEW_* helpers are not explicitly associated to generic metric
definition (as it was already the case before) to avoid ambiguities.
It may change in the future as we may need to use the new definition
method to define static metrics (without the generic bit set). But for
now it isn't the case as this need definition was implemented for generic
metrics support in the first place. If we want to define static metrics
using the API, we could add a new set of helpers for instance.
2025-03-20 11:37:21 +01:00
Christopher Faulet
e87397bc7d BUG/MINOR: mux-h2: Reset streams with NO_ERROR code if full response was already sent
On frontend side, when a stream is shut while the response was already fully
sent, it was cancelled by sending a RST_STREAM(CANCEL) frame. However, it is
not accurrate. CANCEL error code must only be used if the response headers
were sent, but not the full response. As stated in the RFC 9113, when the
response was fully sent, to stop the request sending, a RST_STREAM with an
error code of NO_ERROR must be sent.

This patch should solve the issue #1219. It must be backported to all stable
versions.
2025-03-20 08:36:06 +01:00
William Lallemand
2fb6270910 MEDIUM: ssl/ckch: make the ckch_conf more generic
The ckch_store_load_files() function makes specific processing for
PARSE_TYPE_STR as if it was a type only used for paths.

This patch changes a little bit the way it's done,
PARSE_TYPE_STR is only meant to strdup() a string and stores the
resulting pointer in the ckch_conf structure.

Any processing regarding the path is now done in the callback.

Since the callbacks were basically doing the same thing, they were
transformed into the DECLARE_CKCH_CONF_LOAD() macros which allows to
do some templating of these functions.

The resulting ckch_conf_load_* functions will do the same as before,
except they will also do the path processing instead of letting
ckch_store_load_files() do it, which means we don't need the "base"
member anymore in the struct ckch_conf_kws.
2025-03-19 18:08:40 +01:00
William Lallemand
b0ad777902 MINOR: tools: path_base() concatenates a path with a base path
With the SSL configuration, crt-base, key-base are often used, these
keywords concatenates the base path with the path when the path does not
start by  '/'.

This is done at several places in the code, so a function to do this
would be better to standardize the code.
2025-03-19 17:59:31 +01:00
Aurelien DARRAGON
21601f4a27 BUG/MEDIUM: hlua/cli: fix cli applet UAF in hlua_applet_wakeup()
Recent commit e5e36ce09 ("BUG/MEDIUM: hlua/cli: Fix lua CLI commands
to work with applet's buffers") revealed a bug in hlua cli applet handling

Indeed, playing with Willy's lua tetris script on the cli, a segfault
would be encountered when forcefully closing the session by sending a
CTRL+C on the terminal.

In fact the crash was caused by a UAF: while the cli applet was already
freed, the lua task responsible for waking it up would still point to it.
Thus hlua_applet_wakeup() could be called even if the applet didn't exist
anymore.

To fix the issue, in hlua_cli_io_release_fct() we must also free the hlua
task linked to the applet, like we already do for
hlua_applet_tcp_release() and hlua_applet_http_release().

While this bug exists on stable versions (where it should be backported
too for precaution), it only seems to be triggered starting with 3.0.
2025-03-19 17:03:28 +01:00
Valentine Krasnobaeva
6986e3f41f MINOR: limits: fix check_if_maxsock_permitted description
Fix typo in check_if_maxsock_permitted() description.
2025-03-18 17:38:04 +01:00
Valentine Krasnobaeva
060f441199 BUG/MINOR: limits: compute_ideal_maxconn: don't cap remain if fd_hard_limit=0
'global.fd_hard_limit' stays uninitialized, if haproxy is started with -m
(global.rlimit_memmax). 'remain' is the MAX between soft and hard process fd
limits. It will be always bigger than 'global.fd_hard_limit' (0) in this case.

So, if we reassign 'remain' to the 'global.fd_hard_limit' unconditionally,
calculated then 'maxconn' will be even negative and the DEFAULT_MAXCONN (100)
will be set as the 'ideal_maxconn'.

During the 'global.maxconn' calculations in set_global_maxconn(), if the
provided 'global.rlimit_memmax' is quite big, system will refuse to calculate
based on its 'global.maxconn' and we will do a fallback to the 'ideal_maxconn',
which is 100.

Same problem for the configs with SSL frontends and backends.

This fixes the issue #2899.

This should be backported to v3.1.0.
2025-03-18 17:37:33 +01:00
Willy Tarreau
6336b636f7 MINOR: cli/server: don't take thread isolation to check for srv-removable
Thanks to the previous commits, we now know that "wait srv-removable"
does not require thread isolation, as long as 3372a2ea00 ("BUG/MEDIUM:
queues: Stricly respect maxconn for outgoing connections") and c880c32b16
("MINOR: stream: decrement srv->served after detaching from the list")
are present. Let's just get rid of thread_isolate() here, which can
consume a lot of CPU on highly threaded machines when removing many
servers at once.
2025-03-18 17:36:02 +01:00
Willy Tarreau
aad8e74cb9 CLEANUP: server: make it clear that srv_check_for_deletion() is thread-safe
This function was marked as requiring thread isolation because its code
was extracted from cli_parse_delete_server() and was running under
isolation. But upon closer inspection, and using atomic loads to check
a few counters, it is actually safe to run without isolation, so let's
reflect that in its description.

However, it remains true that cli_parse_delete_server() continues to call
it under isolation.
2025-03-18 17:36:02 +01:00
Willy Tarreau
0e8c573b4b MINOR: server: simplify srv_has_streams()
Now that thanks to commit c880c32b16 ("MINOR: stream: decrement
srv->served after detaching from the list") we can trust srv->served,
let's use it and no longer loop on threads when checking if a server
still has streams attached to it. This will be much cheaper and will
result in keeping isolation for a shorter time in the "wait" command.
2025-03-18 17:36:02 +01:00
Aurelien DARRAGON
4651c4edd5 BUG/MINOR: hlua: fix optional timeout argument index for AppletTCP:receive()
Baptiste reported that using the new optional timeout argument introduced
in 19e48f2 ("MINOR: hlua: add an optional timeout to AppletTCP:receive()")
the following error would occur at some point:

runtime error: file.lua:lineno: bad argument #-2 to 'receive' (number
expected, got light userdata) from [C]: in method 'receive...

In fact this is caused by exp_date being retrieved using relative index -1
instead of absolute index 3. Indeed, while using relative index is fine
most of the time when we trust the stack, when combined with yielding the
top of the stack when resuming from yielding is not necessarily the same
as when the function was first called (ie: if some data was pushed to the
stack in the yieldable function itself). As such, it is safer to use
explicit index to access exp_date variable at position 3 on the stack.

It was confirmed that doing so addresses the issue.

No backport needed unless 19e48f2 is.
2025-03-18 16:48:32 +01:00
Willy Tarreau
c880c32b16 MINOR: stream: decrement srv->served after detaching from the list
In commit 3372a2ea00 ("BUG/MEDIUM: queues: Stricly respect maxconn for
outgoing connections"), it has been ensured that srv->served is held
as long as possible around the periods where a stream is attached to a
server. However, it's decremented early when entering sess_change_server,
and actually just before detaching from that server's list. While there
is theoretically nothing wrong with this, it prevents us from looking at
this counter to know if streams are still using a server or not.

We could imagine decrementing it much later but that wouldn't work with
leastconn, since that algo needs ->served to be final before calling
lbprm.server_drop_conn(). Thus what we're doing here is to detach from
the server, then decrement ->served, and only then call the LB callback
to update the server's position in the tree. At this moment the stream
doesn't know the server anymore anyway (except via this function's
local variable) so it's safe to consider that no stream knows the server
once the variable reaches zero.
2025-03-18 11:43:52 +01:00
Aurelien DARRAGON
7895726bff BUG/MINOR: log: prevent saddr NULL deref in syslog_io_handler()
In ad0133cc ("MINOR: log: handle log-forward "option host""), we
de-reference saddr without first checking if saddr is NULL. In practise
saddr shouldn't be null, but it may be the case if memory error happens
for tcp syslog handler so we must assume that it can be NULL at some
point.

To fix the bug, we simply check for NULL before de-referencing it
under syslog_io_handler(), as the function comment suggests.

No backport needed unless ad0133cc is.
2025-03-18 00:13:19 +01:00
William Lallemand
29b4b985c3 MINOR: jws: use jwt_alg type instead of a char
This patch implements the function EVP_PKEY_to_jws_algo() which returns
a jwt_alg compatible with the private key.

This value can then be passed to jws_b64_protected() and
jws_b64_signature() which modified to take an jwt_alg instead of a char.
2025-03-17 18:06:34 +01:00
Willy Tarreau
19e48f237f MINOR: hlua: add an optional timeout to AppletTCP:receive()
TCP services might want to be interactive, and without a timeout on
receive(), the possibilities are a bit limited. Let's add an optional
timeout in the 3rd argument to possibly limit the wait time. In this
case if the timeout strikes before the requested size is complete,
a possibly incomplete block will be returned.
2025-03-17 16:19:34 +01:00
Valentine Krasnobaeva
557f62593f MINOR: cpu-topo: fix unused stack var 'cpu2' reported by coverity
Coverity has reported that cpu2 seems sometimes unused in
cpu_fixup_topology():

*** CID 1593776:  Code maintainability issues  (UNUSED_VALUE)
/src/cpu_topo.c: 690 in cpu_fixup_topology()
684                             continue;
685
686                     if (ha_cpu_topo[cpu].cl_gid != curr_id) {
687                             if (curr_id >= 0 && cl_cpu <= 2)
688                                     small_cl++;
689                             cl_cpu = 0;
>>>     CID 1593776:  Code maintainability issues  (UNUSED_VALUE)
>>>     Assigning value from "cpu" to "cpu2" here, but that stored value is overwritten before it can be used.
690                             cpu2 = cpu;
691                             curr_id = ha_cpu_topo[cpu].cl_gid;
692                     }
693                     cl_cpu++;
694             }
695

That's it. 'cpu2' automatic/stack variable is used only in for() loop scopes to
save cpus ID in which we are interested in. In the loop pointed by coverity
this variable is not used for further processing within the loop's scope.
Then it is always reinitialized to 0 in the another following loops.

This fixes GitHUb issue #2895.
2025-03-17 14:53:36 +01:00
William Lallemand
de67f25a7e MINOR: jws: add new functions in jws.h
Add signatures of jws_b64_payload(), jws_b64_protected(),
jws_b64_signature(), jws_flattened() which allows to create a complete
JWS flattened object.
2025-03-17 11:51:52 +01:00
Willy Tarreau
e3fd9970a9 MINOR: cpu-topo: add a new "resource" cpu-policy
This cpu policy keeps the smallest CPU cluster. This can
be used to limit the resource usage to the strict minimum
that still delivers decent performance, for example to
try to further reduce power consumption or minimize the
number of cores needed on some rented systems for a
sidecar setup, in order to scale the system down more
easily. Note that if a single cluster is present, it
will still be fully used.

When started on a 64-core EPYC gen3, it uses only one CCX
with 8 cores and 16 threads, all in the same group.
2025-03-14 18:33:16 +01:00
Willy Tarreau
ad3650c354 MINOR: cpu-topo: add a new "efficiency" cpu-policy
This cpu policy tries to evict performant core clusters and only
focuses on efficiency-oriented ones. On an intel i9-14900k, we can
get 525k rps using 8 performance cores, versus 405k when using all
24 efficiency cores. In some cases the power savings might be more
desirable (e.g. scalability tests on a developer's laptop), or the
performance cores might be better suited for another component
(application or security component).
2025-03-14 18:33:16 +01:00
Willy Tarreau
dcae2fa4a4 MINOR: cpu-topo: add a new "performance" cpu-policy
This cpu policy tries to evict efficient core clusters and only
focuses on performance-oriented ones. On an intel i9-14900k, we can
get 525k rps using only 8 cores this way, versus 594k when using all
24 cores. The gains from using all these codes are not significant
enough to waste them on this. Also these cores can be much slower
at doing SSL handshakes so it can make sense to evict them. Better
keep the efficiency cores for network interrupts for example.

Also, on a developer's machine it can be convenient to keep all these
cores for the local tasks and extra tools (load generators etc).
2025-03-14 18:33:16 +01:00
Willy Tarreau
96cd420dc3 MEDIUM: cpu-topo: let the "group-by-cluster" split groups
When a cluster is too large to fit into a single group, let's split it
into two equal groups, which will still be allowed to use all the CPUs
of the cluster. This allows haproxy to start all the threads with a
minimum number of groups (e.g. 2x40 for 80 cores).
2025-03-14 18:33:16 +01:00
Willy Tarreau
8aeb096740 MINOR: cpu-topo: add cpu-policy "group-by-cluster"
This policy forms thread groups from the CPU clusters, and bind all the
threads in them to all the CPUs of the cluster. This is recommended on
system with bad inter-CCX latencies. It was shown to simply triple the
performance with queuing on a 64-core EPYC without having to manually
assign the cores with cpu-map.
2025-03-14 18:33:16 +01:00
Willy Tarreau
aaa4080b8b CLEANUP: thread: now remove the temporary CPU node binding code
This is now superseded by the default "safe" cpu-policy, and every time
it's used, that code was bypassed anyway since global.nbthread was set.
We can now safely remove it. Note that for other policies which do not
set a thread count nor further restrict CPUs (such as "none", or even
"safe" when finding a single node), we continue to go through the fallback
code that automatically assigns CPUs to threads and counts them.
2025-03-14 18:33:16 +01:00
Willy Tarreau
56d939866b MEDIUM: cpu-topo: use the "first-usable-node" cpu-policy by default
This now turns the cpu-policy to "first-usable-node" by default, so that
we preserve the current default behavior consisting in binding to the
first node if nothing was forced. If a second node is found,
global.nbthread is set and the previous code will be skipped.
2025-03-14 18:33:16 +01:00
Willy Tarreau
7fc6cdd0b1 MINOR: cpu-topo: add a 'first-usable-node' cpu policy
This is a reimplemlentation of the current default policy. It binds to
the first node having usable CPUs if found, and drops CPUs from the
second and next nodes.
2025-03-14 18:33:16 +01:00
Willy Tarreau
156430ceb6 MINOR: cpu-topo: add a CPU policy setting to the global section
We'll need to let the user decide what's best for their workload, and in
order to do this we'll have to provide tunable options. For that, we're
introducing struct ha_cpu_policy which contains a name, a description
and a function pointer. The purpose will be to use that function pointer
to choose the best CPUs to use and now to set the number of threads and
thread-groups, that will be called during the thread setup phase. The
only supported policy for now is "none" which doesn't set/touch anything
(i.e. all available CPUs are used).
2025-03-14 18:33:16 +01:00
Willy Tarreau
9a8e8af11a MINOR: cpu-topo: add "only-cluster" and "drop-cluster" to cpu-set
These are processed after the topology is detected, and they allow to
restrict binding to or evict CPUs matching the indicated hardware
cluster number(s). It can be used to bind to only some clusters, such
as CCX or different energy efficiency cores. For this reason, here we
use the cluster's local ID (local to the node).
2025-03-14 18:33:16 +01:00
Willy Tarreau
a946cfa8b5 MINOR: cpu-topo: add "only-core" and "drop-core" to cpu-set
These are processed after the topology is detected, and they allow to
restrict binding to or evict CPUs matching the indicated hardware
core number(s). It can be used to bind to only some clusters as well
as to evict efficient cores whose number is known.
2025-03-14 18:33:16 +01:00
Willy Tarreau
c591c9d6a6 MINOR: cpu-topo: add "only-thread" and "drop-thread" to cpu-set
These are processed after the topology is detected, and they allow to
restrict binding to or evict CPUs matching the indicated hardware
thread number(s). It can be used to reserve even threads for HW IRQs
and odd threads for haproxy for example, or to evict efficient cores
that do only have thread #0.
2025-03-14 18:33:16 +01:00
Willy Tarreau
c93ee25054 MINOR: cpu-topo: add "only-node" and "drop-node" to cpu-set
These are processed after the topology is detected, and they allow to
restrict binding to or evict CPUs matching the indicated node(s).
2025-03-14 18:33:16 +01:00
Willy Tarreau
7263366606 MINOR: cpu-topo: ignore excess of too small clusters
On some Arm systems (typically A76/N1) where CPUs can be associated in
pairs, clusters are reported while they have no incidence on I/O etc.
Yet it's possible to have tens of clusters of 2 CPUs each, which is
counter productive since it does not even allow to start enough threads.

Let's detect this situation as soon as there are at least 4 clusters
having each 2 CPUs or less, which is already very suspcious. In this
case, all these clusters will be reset as meaningless. In the worst
case if needed they'll be re-assigned based on L2/L3.
2025-03-14 18:33:12 +01:00
Willy Tarreau
aa4776210b MINOR: cpu-topo: create an array of the clusters
The goal here is to keep an array of the known CPU clusters, because
we'll use that often to decide of the performance of a cluster and
its relevance compared to other ones. We'll store the number of CPUs
in it, the total capacity etc. For the capacity, we count one unit
per core, and 1/3 of it per extra SMT thread, since this is roughly
what has been measured on modern CPUs.

In order to ease debugging, they're also dumped with -dc.
2025-03-14 18:30:31 +01:00
Willy Tarreau
204ac3c0b6 MINOR: cpu-topo: consider capacity when forming clusters
By using the cluster+capacity sorting function we can detect
heterogneous clusters which are not properly reported. Thanks to this,
the following misnumbered machine featuring 4 big cores, 4 medium ones
an 4 small ones is properly detected with its clusters correctly
assigned:

      [keep] thr=  0 -> cpu=  0 pk=00 no=00 cl=000 ts=000 capa=1024
      [keep] thr=  1 -> cpu=  1 pk=00 no=00 cl=002 ts=008 capa=278
      [keep] thr=  2 -> cpu=  2 pk=00 no=00 cl=002 ts=009 capa=278
      [keep] thr=  3 -> cpu=  3 pk=00 no=00 cl=002 ts=010 capa=278
      [keep] thr=  4 -> cpu=  4 pk=00 no=00 cl=002 ts=011 capa=278
      [keep] thr=  5 -> cpu=  5 pk=00 no=00 cl=001 ts=004 capa=905
      [keep] thr=  6 -> cpu=  6 pk=00 no=00 cl=001 ts=005 capa=905
      [keep] thr=  7 -> cpu=  7 pk=00 no=00 cl=001 ts=006 capa=866
      [keep] thr=  8 -> cpu=  8 pk=00 no=00 cl=001 ts=007 capa=866
      [keep] thr=  9 -> cpu=  9 pk=00 no=00 cl=000 ts=001 capa=984
      [keep] thr= 10 -> cpu= 10 pk=00 no=00 cl=000 ts=002 capa=984
      [keep] thr= 11 -> cpu= 11 pk=00 no=00 cl=000 ts=003 capa=1024

Also this has the benefit of always assigning highest performance
clusters with the smallest IDs so that simple configs can decide to
simply bind to cluster 0 or clusters 0,1 and benefit from optimal
performance.
2025-03-14 18:30:31 +01:00
Willy Tarreau
4a6eaf6c5e MINOR: cpu-topo: add a function to sort by cluster+capacity
The purpose here is to detect heterogenous clusters which are not
properly reported, based on the exposed information about the cores
capacity. The algorithm here consists in sorting CPUs by capacity
within a cluster, and considering as equal all those which have 5%
or less difference in capacity with the previous one. This allows
large clusters of more than 5% total between extremities, while
keeping apart those where the limit is more pronounced. This is
quite common in embedded environments with big.little systems, as
well as on some laptops.
2025-03-14 18:30:31 +01:00
Willy Tarreau
0290b807dd MINOR: cpu-topo: renumber cores to avoid holes and make them contiguous
Due to the way core numbers are assigned and the presence of SMT on
some of them, some holes may remain in the array. Let's renumber them
to plug holes once they're known, following pkg/node/die/llc etc, so
that they're local to a (pkg,node) set. Now an i7-14700 shows cores
0 to 19, not 0 to 27.
2025-03-14 18:30:31 +01:00
Willy Tarreau
b633b9d422 MINOR: cpu-topo: assign an L3 cache if more than 2 L2 instances
On some machines, L3 is not always reported (e.g. on some lx2 or some
armada8040). But some also don't have L3 (core 2 quad). However, no L3
when there are more than 2 L2 is quite unheard of, and while we don't
really care about firing 2 thread groups for 2 L2, we'd rather avoid
doing this if there are 8! In this case we'll declare an L3 instance
to fix the situation. This allows small machines to continue to start
with two groups while not derivating on large ones.
2025-03-14 18:30:31 +01:00
Willy Tarreau
d169758fa9 MINOR: cpu-topo: make sure we don't leave unassigned IDs in the cpu_topo
It's important that we don't leave unassigned IDs in the topology,
because the selection mechanism is based on index-based masks, so an
unassigned ID will never be kept. This is particularly visible on
systems where we cannot access the CPU topology, the package id, node id
and even thread id are set to -1, and all CPUs are evicted due to -1 not
being set in the "only-cpu" sets.

Here in new function "cpu_fixup_topology()", we assign them with the
smallest unassigned value. This function will be used to assign IDs
where missing in general.
2025-03-14 18:30:31 +01:00
Willy Tarreau
af648c7b58 MINOR: cpu-topo: assign clusters to cores without and renumber them
Due to the previous commit we can end up with cores not assigned
any cluster ID. For this, at the end we sort the CPUs by topology
and assign cluster IDs to remaining CPUs based on pkg/node/llc.
For example an 14900 now shows 5 clusters, one for the 8 p-cores,
and 4 of 4 e-cores each.

The local cluster numbers are per (node,pkg) ID so that any rule could
easily be applied on them, but we also keep the global numbers that
will help with thread group assignment.

We still need to force to assign distinct cluster IDs to cores
running on a different L3. For example the EPYC 74F3 is reported
as having 8 different L3s (which is true) and only one cluster.

Here we introduce a new function "cpu_compose_clusters()" that is called
from the main init code just after cpu_detect_topology() so that it's
not OS-dependent. It deals with this renumbering of all clusters in
topology order, taking care of considering any distinct LLC as being
on a distinct cluster.
2025-03-14 18:30:31 +01:00
Willy Tarreau
385360fe81 MINOR: cpu-topo: ignore single-core clusters
Some platforms (several armv7, intel 14900 etc) report one distinct
cluster per core. This is problematic as it cannot let clusters be
used to distinguish real groups of cores, and cannot be used to build
thread groups.

Let's just compare the cluster cpus to the siblings, and ignore it if
they exactly match. We must also take care of not falling back to
core_cpus_list, which can enumerate cores that already have their
cluster assigned (e.g. intel 14900 has 4 4-Ecore clusters in addition
to the 8 Pcores).
2025-03-14 18:30:31 +01:00
Willy Tarreau
a4471ea56d MINOR: cpu-topo: implement a CPU sorting mechanism by cluster ID
This will be used to detect and fix incorrect setups which report
the same cluster ID for multiple L3 instances.

The arrangement of functions in this file is becoming a real problem.
Maybe we should move all this to cpu_topo for example, and better
distinguish OS-specific and generic code.
2025-03-14 18:30:31 +01:00
Willy Tarreau
a8acdbd9fd MINOR: cpu-topo: implement a sorting mechanism by CPU locality
Once we've kept only the CPUs we want, the next step will be to form
groups and these ones are based on locality. Thus we'll have to sort by
locality. For now the locality is only inferred by the index. No grouping
is made at this point. For this we add the "cpu_reorder_by_locality"
function with a locality-based comparison function.
2025-03-14 18:30:31 +01:00
Willy Tarreau
18133a054d MINOR: cpu-topo: implement a sorting mechanism for CPU index
CPU selection will be performed by sorting CPUs according to
various criteria. For dumps however, that's really not convenient
and we'll need to reorder the CPUs according to their index only.
This is what the new function cpu_reorder_by_index() does. It's
called  in thread_detect_count() before dumping the CPU topology.
2025-03-14 18:30:31 +01:00
Willy Tarreau
661d49a18a MINOR: cpu-topo: skip CPU properties that we've verified do not exist
A number of entries under /cpu/cpu%d only exist on certain kernel
versions, certain archs and/or with certain modules loaded. It's
pointless to insist on trying to read them all for all CPUs when
we've already verified they do not exist. Thus let's use stat()
the first time prior to checking some of them, and only try to
access them when they really exist. This almost completely
eliminates the large number of ENOENT that was visible in strace
during startup.
2025-03-14 18:30:31 +01:00
Willy Tarreau
baeea08dba MINOR: cpu-topo: skip identification of non-existing CPUs
There's no point trying to read all entries under /cpu/cpu%d when that
one does not exist, so let's just skip it in this case.
2025-03-14 18:30:31 +01:00
Willy Tarreau
8542c79f9d MINOR: cpu-topo: skip CPU detection when /sys/.../cpu does not exist
There's no point scanning all entries when /cpu doesn't exist in the
first place. Let's check once for it and skip the loop in this case.
2025-03-14 18:30:30 +01:00
Willy Tarreau
c5ddf4a5b2 MINOR: cpu-topo: boost the capacity of performance cores with cpufreq
Cpufreq alone isn't a good metric on heterogenous CPUs because efficient
cores can reach almost as high frequencies as performant ones. Tests have
shown that majoring performance cores by 50% gives a pretty accurate
estimate of the performance to expect on modern CPUs, and that counting
+33% per extra SMT thread is reasonable as well. We don't have the info
about the core's quality, but using the presence of SMT is a reasonable
approach in this case, given that efficiency cores will not use it.

As an example, using one thread of each of the 8 P-cores of an intel
i9-14900k gives 395k rps for a corrected total capacity of 69.3k, using
the 16 E-cores gives 40.5k for a total capacity of 70.4k, and using both
threads of 6 P-cores gives 41.1k for a total capacity of 69.6k. Thus the
3 same scores deliver the same performance in various combinations.
2025-03-14 18:30:30 +01:00
Willy Tarreau
e4aa13e786 MINOR: cpu-topo: use cpufreq before acpi cppc
The acpi_cppc method was found to take about 5ms per CPU on a 64-core
EPYC system, which is plain unacceptable as it delays the boot by half
a second. Let's use the less accurate cpufreq first, which should be
sufficient anyway since many systems do not have acpi_cppc. We'll only
fall back to acpi_cppc for systems without cpufreq. If it were to be
an issue over time, we could also automatically consider that all
threads of the same core or even of the same cluster run at the same
speed (when a cluster is known to be accurate).
2025-03-14 18:30:30 +01:00
Willy Tarreau
d11241b7ba MINOR: cpu-topo: fall back to nominal_perf and scaling_max_freq for the capacity
When cpu_capacity is not present, let's try to check acpi_cppc's
nominal_perf which is similar and commonly found on servers, then
scaling_max_freq (though that last one may vary a bit between CPUs
depending on die quality). That variation is not a problem since
we can absorb a ~5% variation without issue.

It was verified on an i9-14900 featuring 5.7-P, 6.0-P and 4.4-E GHz
that P-cores were not reordered and that E cores were placed last.
It was also OK on a W3-2345 with 4.3 to 4.5GHz.
2025-03-14 18:30:30 +01:00
Willy Tarreau
322c28cc19 MINOR: cpu-topo: refine cpu dump output to better show kept/dropped CPUs
It's becoming difficult to see which CPUs are going to be kept/dropped.
Let's just skip all offline CPUs, and indicate "keep" in front of those
that are going to be used, and "----" in front of the excluded ones. It
is way more readable this way.

Also let's just drop the array entry number, since it's always the same
as the CPU number and is only an internal representation anyway.
2025-03-14 18:30:30 +01:00
Willy Tarreau
f1210ee7c6 MEDIUM: cfgparse: remove now unused numa & thread-count detection
Ths is not needed anymore since already done before landing here
via thread_detect_count().
2025-03-14 18:30:30 +01:00
Willy Tarreau
e3aef4c9a4 MEDIUM: thread: reimplement first numa node detection
Let's reimplement automatic binding to the first NUMA node when thread
count is not forced. It's the same thing as is already done in
check_config_validity() except that this time it's based on the
collected CPU information. The threads are automatically counted
and CPUs from non-first node(s) are evicted.
2025-03-14 18:30:30 +01:00
Willy Tarreau
4a525e8d27 MEDIUM: cpu-topo: make sure to properly assign CPUs to threads as a fallback
If no cpu-map is done and no cpu-policy could be enforced, we still need
to count the number of usable CPUs, assign them to all threads and set
the nbthread value accordingly.

This already handles the part that was done in check_config_validity()
via thread_cpus_enabled_at_boot.
2025-03-14 18:30:30 +01:00
Willy Tarreau
1af4942c95 MEDIUM: thread: start to detect thread groups and threads min/max
By mutually refining the thread count and group count, we can try
to detect the most suitable setup for the current machine. Taskset
is implicitly handled correctly. tgroups automatically adapt to the
configured number of threads. cpu-map manages to limit tgroups to
the smallest supported value.

The thread-limit is enforced. Just like in cfgparse, if the thread
count was forced to a higher value, it's reduced and a warning is
emitted. But if it was not set, the thr_max value is bound to this
limit so that further calculations respect it.

We continue to default to the max number of available threads and 1
tgroup by default, with the limit. This normally allows to get rid
of that test in check_config_validity().
2025-03-14 18:30:30 +01:00
Willy Tarreau
68069e4b27 MINOR: cpu-topo: add "drop-cpu" and "only-cpu" to cpu-set
These allow respectively to disable binding to CPUs listed in a set, and
to disable binding to CPUs not in a set.
2025-03-14 18:30:30 +01:00
Willy Tarreau
cda4956d9c MINOR: cpu-topo: add a new "cpu-set" global directive to choose cpus
For now it's limited, it only supports "reset" to ask that any previous
"taskset" be ignored. The goal will be to later add more actions that
allow to symbolically define sets of cpus to bind to or to drop. This
also clears the cpu_mask_forced variable that is used to detect
that a taskset had been used.
2025-03-14 18:30:30 +01:00
Willy Tarreau
f0661e79fe MINOR: global: add a command-line option to enable CPU binding debugging
During development, everything related to CPU binding and the CPU topology
is debugged using state dumps at various places, but it does make sense to
have a real command line option so that this remains usable in production
to help users figure why some CPUs are not used by default. Let's add
"-dc" for this. Since the list of global.tune.options values is almost
full and does not 100% match this option, let's add a new "tune.debug"
field for this.
2025-03-14 18:30:30 +01:00
Willy Tarreau
94543d7b65 MINOR: cfgparse: use already known offline CPU information
No need to reparse cpu/online, let's just rely on the info we learned
previously about offline CPUs.
2025-03-14 18:30:30 +01:00
Willy Tarreau
1560827c9d MINOR: cfgparse: move the binding detection into numa_detect_topology()
For now the function refrains from detecting the CPU topology when a
restrictive taskset or cpu-map was already performed on the process,
and it's documented as such, the reason being that until we're able
to automatically create groups, better not change user settings. But
we'll need to be able to detect bound CPUs and to process them as
desired by the user, so we now need to move that detection into the
function itself. It changes nothing to the logic, just gives more
freedom to the function.
2025-03-14 18:30:30 +01:00
Willy Tarreau
ac1db9db7d MINOR: thread: turn thread_cpu_mask_forced() into an init-time variable
The function is not convenient because it doesn't allow us to undo the
startup changes, and depending on where it's being used, we don't know
whether the values read have already been altered (this is not the case
right now but it's going to evolve).

Let's just compute the status during cpu_detect_usable() and set a
variable accordingly. This way we'll always read the init value, and
if needed we can even afford to reset it. Also, placing it in cpu_topo.c
limits cross-file dependencies (e.g. threads without affinity etc).
2025-03-14 18:30:30 +01:00
Willy Tarreau
3a7cc676fa MINOR: cpu-topo: add NUMA node identification to CPUs on FreeBSD
With this patch we're also NUMA node IDs to each CPU when the info is
found. The code is highly inspired from the one in commit f5d48f8b3
("MEDIUM: cfgparse: numa detect topology on FreeBSD."), the difference
being that we're just setting the value in ha_cpu_topo[].
2025-03-14 18:30:30 +01:00
Willy Tarreau
f6154c079e MINOR: cpu-topo: add NUMA node identification to CPUs on Linux
With this patch we're also assigning NUMA node IDs to each CPU when one
is found. The code is highly inspired from the one in commit b56a7c89a
("MEDIUM: cfgparse: detect numa and set affinity if needed") that already
did the job, except that it could be simplified since we're just collecting
info to fill the ha_cpu_topo[] array.
2025-03-14 18:30:30 +01:00
Willy Tarreau
65612369e7 MINOR: cpu-topo: also store the sibling ID with SMT
The sibling ID was not reported because it's not directly accessible
but we don't care, what matters is that we assign numbers to all the
threads we find using the same CPU so that some strategies permit to
allocate one thread at a time if we want to use few threads with max
performance.
2025-03-14 18:30:30 +01:00
Willy Tarreau
7cb274439b MINOR: cpu-topo: add CPU topology detection for linux
This uses the publicly available information from /sys to figure the cache
and package arrangements between logical CPUs and fill ha_cpu_topo[], as
well as their SMT capabilities and relative capacity for those which expose
this. The functions clearly have to be OS-specific.
2025-03-14 18:30:30 +01:00
Willy Tarreau
12f3a2bbb7 MINOR: cpu-topo: try to detect offline cpus at boot
When possible, the offline CPUs are detected at boot and their OFFLINE
flag is set in the ha_cpu_topo[] array. When the detection is not
possible (e.g. not linux, /sys not mounted etc), we just mark none of
them as being offline, as we don't want to infer wrong info that could
hinder automatic CPU placement detection. When valid, we take this
opportunity for refining cpu_topo_lastcpu so that we don't need to
manipulate CPUs beyond this value.
2025-03-14 18:30:30 +01:00
Willy Tarreau
44881e5abf MINOR: cpu-topo: add detection of online CPUs on FreeBSD
On FreeBSD we can detect online CPUs at least by doing the bitwise-OR of
the CPUs of all domains, so we're using this and adding this detection
to ha_cpuset_detect_online(). If we find simpler later, we can always
rework it, but it's reasonably inexpensive since we only check existing
domains.
2025-03-14 18:30:30 +01:00
Willy Tarreau
8f72ce335a MINOR: cpu-topo: add detection of online CPUs on Linux
This adds a generic function ha_cpuset_detect_online() which for now
only supports linux via /sys. It fills a cpuset with the list of online
CPUs that were detected (or returns a failure).
2025-03-14 18:30:30 +01:00
Willy Tarreau
8c524c7c9d REORG: cpu-topo: move bound cpu detection from cpuset to cpu-topo
The cpuset files are normally used only for cpu manipulations. It happens
that the initial CPU binding detection was initially placed there since
there was no better place, but in practice, being OS-specific, it should
really be in cpu-topo. This simplifies cpuset which doesn't need to know
about the OS anymore.
2025-03-14 18:30:30 +01:00
Willy Tarreau
a6fdc3eaf0 MINOR: cpu-topo: update CPU topology from excluded CPUs at boot
Now before trying to resolve the thread assignment to groups, we detect
which CPUs are not bound at boot so that we can mark them with
HA_CPU_F_EXCLUDED. This will be useful to better know on which CPUs we
can count later. Note that we purposely ignore cpu-map here as we
don't know how threads and groups will map to cpu-map entries, hence
which CPUs will really be used.

It's important to proceed this way so that when we have no info we
assume they're all available.
2025-03-14 18:30:30 +01:00
Willy Tarreau
bdb731172c MINOR: cpu-topo: add a function to dump CPU topology
The new function cpu_dump_topology() will centralize most debugging
calls, and it can make efforts of not dumping some possibly irrelevant
fields (e.g. non-existing cache levels).
2025-03-14 18:30:30 +01:00
Willy Tarreau
041462c4af MINOR: cpu-topo: rely on _SC_NPROCESSORS_CONF to trim maxcpus
We don't want to constantly deal with as many CPUs as a cpuset can hold,
so let's first try to trim the value to what the system claims to support
via _SC_NPROCESSORS_CONF. It is obviously still subject to the limit of
the cpuset size though. The value is stored globally so that we can
reuse it elsewhere after initialization.
2025-03-14 18:30:30 +01:00
Willy Tarreau
656cedad42 MINOR: cpu-topo: allocate and initialize the ha_cpu_topo array.
This does the bare minimum to allocate and initialize a global
ha_cpu_topo array for the number of supported CPUs and release
it at deinit time.
2025-03-14 18:30:30 +01:00
Willy Tarreau
d165f5d3ab MINOR: cpu-topo: add ha_cpu_topo definition
This structure will be used to store information about each CPU's
topology (package ID, L3 cache ID, NUMA node ID etc). This will be used
in conjunction with CPU affinity setting to try to perform a mostly
optimal binding between threads and CPU numbers by default. Since it
was noticed during tests that absolutely none of the many machines
tested reports different die numbers, the die_id is not stored.
Also, it was found along experiments that the cluster ID will be used
a lot, half of the time as a node-local identifier, and half of the
time as a global identifier. So let's store the two versions at once
(cl_gid, cl_lid).

Some flags are added to indicate causes of exclusion (offline, excluded
at boot, excluded by rules, ignored by policy).
2025-03-14 18:30:30 +01:00
Willy Tarreau
05a4efb102 MINOR: thread: rely on the cpuset functions to count bound CPUs
let's just clean up the thread_cpus_enabled() code a little bit
by removing the OS-specific code and rely on ha_cpuset_detect_bound()
instead. On macos we continue to use sysconf() for now.
2025-03-14 18:30:30 +01:00
Willy Tarreau
32bb68e736 MINOR: cpuset: make the API support negative CPU IDs
Negative IDs are very convenient to mean "not set", so let's just make
the cpuset API robust against this, especially with ha_cpuset_isset()
so that we don't have to manually add this check everywhere when a
value is not known.
2025-03-14 18:30:30 +01:00
Willy Tarreau
f156baf8ce DOC: design-thoughts: commit numa-auto.txt
Lots of collected data and observations aggregated into a single commit
so as not to lose them. Some parts below come from several commit
messages and are incremental.

Add captures and analysis of intel 14900 where it's not easy to draw
the line between the desired P and E cores.

The 14900 raises some questions (imagine a dual-die variant in multi-socket).
That's the start of an algorithmic distribution of performance cores into
thread groups.

cpu-map currently conflicts a lot with the choices after auto-detection
but it doesn't have to. The problem is the inability to configure the
threads for the whole process like taskset does. By offering this ability
we can also start to designate groups of CPUs symbolically (package, die,
ccx, cores, smt).

It can also be useful to exploit the info from cpuinfo that is not
available in /sys, such as the model number. At least on arm, higher
numbers indicate bigger cores and can be useful to distinguish cores
inside a cluster. It will not indicate big vs medium ones of the same
type (e.g. a78 3.0 vs 2.4 GHz) but can still be effective at identifying
the efficient ones.

In short, infos such as cluster ID not always reliable, and are
local to the package. die_id as well. die number is not reported
here but should definitely be used, as a higher priority than L3.

We're still missing a discriminant between the l3 and cluster number
in order to address heterogenous CPUs (e.g. intel 14900), though in
terms of locality that's currently done correctly.

CPU selection is also a full topic, and some thoughts were noted
regarding sorting by perf vs locality so as never to mix inter-
socket CPUs due to sorting.

The proposed cpu-selection cannot work as-is, because it acts both on
restriction and preference, and these two are not actions but a sequence.
First restrictions must be enforced, and second the remaining CPUs are
sorted according to the preferred criterion, and a number of threads are
selected.

Currently we refine the OS-exposed cluster number but it's not correct
as we can end up with something poorly numbered. We need to respect the
LLC in any case so let's explain the approach.
2025-03-14 18:30:30 +01:00
Willy Tarreau
0ceb1f2c51 DEV: ncpu: also emulate sysconf() for _SC_NPROCESSORS_*
This is also needed in order to make the requested number of CPUs
appear. For now we don't reroute to the original sysconf() call so
we return -1,EINVAL for all other info.
2025-03-14 18:30:30 +01:00
Willy Tarreau
ed75148ca0 BUILD: tools: avoid a build warning on gcc-4.8 in resolve_sym_name()
A build warning is emitted with gcc-4.8 in tools.c since commit
e920d73f59 ("MINOR: tools: improve symbol resolution without dl_addr")
because the compiler doesn't see that <size> is necessarily initialized.
Let's just preset it.
2025-03-14 18:30:30 +01:00
Willy Tarreau
4e09789644 MINOR: tools: teach resolve_sym_name() a few more common symbols
This adds run_poll_loop, run_tasks_from_lists, process_runnable_tasks,
ha_dump_backtrace and cli_io_handler which are fairly common in
backtraces. This will be less relative symbols when dladdr is not
usable.
2025-03-13 17:31:16 +01:00
Willy Tarreau
a3582a77f7 MINOR: tools: ease the declaration of known symbols in resolve_sym_name()
Let's have a macro that declares both the symbol and its name, it will
avoid the risk of introducing typos, and encourages adding more when
needed. The macro also takes an optional second argument to permit an
inline declaration of an extern symbol.
2025-03-13 17:30:48 +01:00
Willy Tarreau
e920d73f59 MINOR: tools: improve symbol resolution without dl_addr
When dl_addr is not usable or fails, better fall back to the closest
symbol among the known ones instead of providing everything relative
to main. Most often, the location of the function will give some hints
about what it can be. Thus now we can emit fct+0xXXX in addition to
main+0xXXX or main-0xXXX. We keep a margin of +256kB maximum after a
function for a match, which is around the maximum size met in an object
file, otherwise it becomes pointless again.
2025-03-13 17:30:48 +01:00
Willy Tarreau
1e99efccef MINOR: cli: export cli_io_handler() to ease symbol resolution
It's common to meet this function in backtraces, it's a bit annoying
that it's not resolved, so let's export it so that it becomes resolvable.
2025-03-13 17:30:48 +01:00
Aurelien DARRAGON
8311be5ac6 BUG/MINOR: stats: fix capabilities and hide settings for some generic metrics
Performing a diff on stats output before vs after commit 66152526
("MEDIUM: stats: convert counters to new column definition") revealed
that some metrics were not properly ported to to the new API. Namely,
"lbtot", "cli_abrt" and "srv_abrt" are now exposed on frontend and
listeners while it was not the case before.

Also, "hrsp_other" is exposed even when "mode http" wasn't set on the
proxy.

In this patch we restore original behavior by fixing the capabilities
and hide settings.

As this could be considered as a minor regression (looking at the commit
message it doesn't seem intended), better tag this as a bug. It should be
backported in 3.0 with 66152526.
2025-03-13 11:49:18 +01:00
Aurelien DARRAGON
4c3eb60e70 DOC: management: rename some last occurences from domain "dns" to "resolvers"
This is a complementary patch to cf913c2f9 ("DOC: management: rename show
stats domain cli "dns" to "resolvers"). The doc still refered to the
legacy "dns" domain filter for stat command. Let's rename those occurences
to "resolvers".

It may be backported to all stable versions.
2025-03-13 11:49:10 +01:00
Willy Tarreau
78ef52dbd1 BUILD: backend: silence a build warning when threads are disabled
Since commit 8de8ed4f48 ("MEDIUM: connections: Allow taking over
connections from other tgroups.") we got this partially absurd
build warning when disabling threads:

  src/backend.c: In function 'conn_backend_get':
  src/backend.c:1371:27: warning: array subscript [0, 0] is outside array bounds of 'struct tgroup_info[1]' [-Warray-bounds]

The reason is that gcc sees that curtgid is not equal to tgid which is
defined as 1 in this case, thus it figures that tgroup_info[curtgid-1]
will be anything but zero and that doesn't fit. It is ridiculous as it
is a perfect case of dead code elimination which should not warrant a
warning. Nevertheless we know we don't need to do this when threads are
disabled and in this case there will not be more than 1 thread group, so
we can happily use that preliminary test to help the compiler eliminate
the dead condition and avoid spitting this warning.

No backport is needed.
2025-03-12 18:16:14 +01:00
Willy Tarreau
b61ed9babe BUILD: tools: silence a build warning when USE_THREAD=0
The dladdr_lock that was added to avoid re-entering into dladdr is
conditioned by threads, but the way it's declared causes a build
warning if threads are disabled due to the insertion of a lone semi
colon in the variables block. Let's switch to __decl_thread_var()
for this.

This can be backported wherever commit eb41d768f9 ("MINOR: tools:
use only opportunistic symbols resolution") is backported. It relies
on these previous two commits:

   bb4addabb7 ("MINOR: compiler: add a simple macro to concatenate resolved strings")
   69ac4cd315 ("MINOR: compiler: add a new __decl_thread_var() macro to declare local variables")
2025-03-12 18:11:14 +01:00
Willy Tarreau
69ac4cd315 MINOR: compiler: add a new __decl_thread_var() macro to declare local variables
__decl_thread() already exists but is more suited for struct members.
When using it in a variables block, it appends the final trailing
semi-colon which is a statement that ends the variable block. Better
clean this up and have one precisely for variable blocks. In this
case we can simply define an unused enum value that will consume the
semi-colon. That's what the new macro __decl_thread_var() does.
2025-03-12 18:08:12 +01:00
Willy Tarreau
bb4addabb7 MINOR: compiler: add a simple macro to concatenate resolved strings
It's often useful to be able to concatenate strings after resolving
them (e.g. __FILE__, __LINE__ etc). Let's just have a CONCAT() macro
to do that, which calls _CONCAT() with the same arguments to make
sure the contents are resolved before being concatenated.
2025-03-12 18:06:55 +01:00
Willy Tarreau
12383fd9f5 BUG/MEDIUM: thread: use pthread_self() not ha_pthread[tid] in set_affinity
A bug was uncovered by the work on NUMA. It only triggers in the CI
with libmusl due to a race condition. What happens is that the call
to set_thread_cpu_affinity() is done very early in the polling loop,
and that it relies on ha_pthread[tid] instead of pthread_self(). The
problem is that ha_pthread[tid] is only set by the return from
pthread_create(), which might happen later depending on the number of
CPUs available to run the starting thread.

Let's just use pthread_self() here. ha_pthread[] is only used to send
signals between threads, there's no point in using it here.

This can be backported to 2.6.
2025-03-12 15:59:23 +01:00
Aurelien DARRAGON
e942305214 MEDIUM: log: change default "host" strategy for log-forward section
Historically, log-forward proxy used to preserve host field from input
message as much as possible, and if syslog host wasn't provided
(rfc5424 '-' or bad rfc3164 or rfc5424 message) then "localhost" or "-"
would be used as host when outputting message using rfc3164 or rfc5424.

We change that behavior (which corresponds to "keep" host option), so that
log-forward now uses "fill" strategy as default: if the host is provided
in input message, it is preserved. However if it is missing and IP address
from sender is available, we use it.
2025-03-12 10:55:49 +01:00
Aurelien DARRAGON
ad0133cc50 MINOR: log: handle log-forward "option host"
Following previous patch, we know implement the logic for the host
option under log-forward section. Possible strategies are:

      replace If input message already contains a value for the host
              field, we replace it by the source IP address from the
              sender.
              If input message doesn't contain a value for the host field
              (ie: '-' as input rfc5424 message or non compliant rfc3164
              or rfc5424 message), we use the source IP address from the
              sender as host field.

      fill    If input message already contains a value for the host field,
              we keep it.
              If input message doesn't contain a value for the host field
              (ie: '-' as input rfc5424 message or non compliant rfc3164
              or rfc5424 message), we use the source IP address from the
              sender as host field.

      keep    If input message already contains a value for the host field,
              we keep it.
              If input message doesn't contain a value for the host field,
              we set it to localhost (rfc3164) or '-' (rfc5424).
              (This is the default)

      append  If input message already contains a value for the host field,
              we append a comma followed by the IP address from the sender.
              If input message doesn't contain a value for the host field,
              we use the source IP address from the sender.

Default value (unchanged) is "keep" strategy. option host is only relevant
with rfc3164 or rfc5424 format on log targets. Also, if the source address
is not available (ie: UNIX socket), default behavior prevails.

Documentation was updated.
2025-03-12 10:52:07 +01:00
Aurelien DARRAGON
003fe530ae MINOR: log: add "option host" log-forward option
add only the parsing part, options are currently unused
2025-03-12 10:51:35 +01:00
Aurelien DARRAGON
47f14be9f3 MINOR: tools: only print address in sa2str() when port == -1
Support special value for port in sa2str: if port is equal to -1, only
print the address without the port, also ignoring <map_ports> value.
2025-03-12 10:51:20 +01:00
Aurelien DARRAGON
2de62d0461 MINOR: log: provide source address information in syslog_process_message()
provide struct sockaddr_storage pointer from the message sender in
syslog_process_message()
2025-03-12 10:50:30 +01:00
Aurelien DARRAGON
bc76f6dde9 MINOR: log: migrate log-forward options from proxy->options2 to options3
Migrate recently added log-forward section options, currently stored under
proxy->options2 to proxy->options3 since proxy->options2 is running out of
space and we plan on adding more log-forward options.
2025-03-12 10:50:03 +01:00
Aurelien DARRAGON
cc5a66212d MINOR: proxy: add proxy->options3
proxy->options2 is almost full, yet we will add new log-forward options
in upcoming patches so we anticipate that by adding a new {no_}options3
and cfg_opts3[] to further extend proxy options
2025-03-12 10:49:36 +01:00
Aurelien DARRAGON
d47e7103b8 CLEANUP: log: add syslog_process_message() helper
Prevent code duplication under syslog_fd_handler() and syslog_io_handler()
by merging common code path in a single syslog_process_message() helper
that processed a single message stored in <buf> according to <frontend>
settings.
2025-03-12 10:49:18 +01:00
Aurelien DARRAGON
8b8520305e CLEANUP: log-forward: remove useless options2 init
It is actually not required to zero out proxy->options2 since proxy is
allocated using calloc() which already does it.
2025-03-12 10:49:08 +01:00
William Lallemand
c6e6318125 CI: github: add "jose" to apt dependencies
jose is used in the JWS unit-test, let's add it to the CI.
2025-03-11 22:29:40 +01:00
William Lallemand
d014d7ee72 TESTS: jws: implement a test for JWS signing
This test returns a JWS payload signed a specified private key in the
PEM format, and uses the "jose" command tool to check if the signature
is correct against the jwk public key.

The test could be improved later by using the code from jwt.c allowing
to check a signature.
2025-03-11 22:29:40 +01:00
William Lallemand
3abb428fc8 MINOR: jws: implement JWS signing
This commits implement JWS signing, this is divided in 3 parts:

- jws_b64_protected() creates a JWS "protected" header, which takes the
  algorithm, kid or jwk, nonce and url as input, and fill a destination
  buffer with the base64url version of the header
- jws_b64_payload() just encode a payload in base64url
- jws_b64_signature() generates a signature using as input the protected
  header and the payload, it supports ES256, ES384 and ES512 for ECDSA
  keys, and RS256 for RSA ones. The RSA signature just use the
  EVP_DigestSign() API with its result encoded in base64url. For ECDSA
  it's a little bit more complicated, and should follow section 3.4 of
  RFC7518, R and S should be padded to byte size.

Then the JWS can be output with jws_flattened() which just formats the 3
base64url output in a JSON representation with the 3 fields, protected,
payload and signature.
2025-03-11 22:29:40 +01:00
Willy Tarreau
3cbeb6a74b [RELEASE] Released version 3.2-dev7
Released version 3.2-dev7 with the following main changes :
    - BUG/MEDIUM: applet: Don't handle EOI/EOS/ERROR is applet is waiting for room
    - BUG/MEDIUM: spoe/mux-spop: Introduce an NOOP action to deal with empty ACK
    - BUG/MINOR: cfgparse: fix NULL ptr dereference in cfg_parse_peers
    - BUG/MEDIUM: uxst: fix outgoing abns address family in connect()
    - REGTESTS: fix reg-tests/server/abnsz.vtc
    - BUG/MINOR: log: fix outgoing abns address family
    - BUG/MINOR: sink: add tempo between 2 connection attempts for sft servers
    - MINOR: clock: always use atomic ops for global_now_ms
    - CI: QUIC Interop: clean old docker images
    - BUG/MINOR: stream: do not call co_data() from __strm_dump_to_buffer()
    - BUG/MINOR: mux-h1: always make sure h1s->sd exists in h1_dump_h1s_info()
    - MINOR: tinfo: add a new thread flag to indicate a call from a sig handler
    - BUG/MEDIUM: stream: never allocate connection addresses from signal handler
    - MINOR: freq_ctr: provide non-blocking read functions
    - BUG/MEDIUM: stream: use non-blocking freq_ctr calls from the stream dumper
    - MINOR: tools: use only opportunistic symbols resolution
    - CLEANUP: task: move the barrier after clearing th_ctx->current
    - MINOR: compression: Introduce minimum size
    - BUG/MINOR: h2: always trim leading and trailing LWS in header values
    - MINOR: tinfo: split the signal handler report flags into 3
    - BUG/MEDIUM: stream: don't use localtime in dumps from a signal handler
    - OPTIM: connection: don't try to kill other threads' connection when !shared
    - BUILD: add possibility to use different QuicTLS variants
    - MEDIUM: fd: Wait if locked in fd_grab_tgid() and fd_take_tgid().
    - MINOR: fd: Add fd_lock_tgid_cur().
    - MEDIUM: epoll: Make sure we can add a new event
    - MINOR: pollers: Add a fixup_tgid_takeover() method.
    - MEDIUM: pollers: Drop fd events after a takeover to another tgid.
    - MEDIUM: connections: Allow taking over connections from other tgroups.
    - MEDIUM: servers: Add strict-maxconn.
    - BUG/MEDIUM: server: properly initialize PROXY v2 TLVs
    - BUG/MINOR: server: fix the "server-template" prefix memory leak
    - BUG/MINOR: h3: do not report transfer as aborted on preemptive response
    - CLEANUP: h3: fix documentation of h3_rcv_buf()
    - MINOR: hq-interop: properly handle incomplete request
    - BUG/MEDIUM: mux-fcgi: Try to fully fill demux buffer on receive if not empty
    - MINOR: h1: permit to relax the websocket checks for missing mandatory headers
    - BUG/MINOR: hq-interop: fix leak in case of rcv_buf early return
    - BUG/MINOR: server: check for either proxy-protocol v1 or v2 to send hedaer
    - MINOR: jws: implement a JWK public key converter
    - DEBUG: init: add a way to register functions for unit tests
    - TESTS: add a unit test runner in the Makefile
    - TESTS: jws: register a unittest for jwk
    - CI: github: run make unit-tests on the CI
    - TESTS: add config smoke checks in the unit tests
    - MINOR: jws: conversion to NIST curves name
    - CI: github: remove smoke tests from vtest.yml
    - TESTS: ist: fix wrong array size
    - TESTS: ist: use the exit code to return a verdict
    - TESTS: ist: add a ist.sh to launch in make unit-tests
    - CI: github: fix h2spec.config proxy names
    - DEBUG: init: Add a macro to register unit tests
    - MINOR: sample: allow custom date format in error-log-format
    - CLEANUP: log: removing "log-balance" references
    - BUG/MINOR: log: set proper smp size for balance log-hash
    - MINOR: log: use __send_log() with exact payload length
    - MEDIUM: log: postpone the decision to send or not log with empty messages
    - MINOR: proxy: make pr_mode enum bitfield compatible
    - MINOR: cfgparse-listen: add and use cfg_parse_listen_match_option() helper
    - MINOR: log: add options eval for log-forward
    - MINOR: log: detach prepare from parse message
    - MINOR: log: add dont-parse-log and assume-rfc6587-ntf options
    - BUG/MEIDUM: startup: return to initial cwd only after check_config_validity()
    - TESTS: change the output of run-unittests.sh
    - TESTS: unit-tests: store sh -x in a result file
    - CI: github: show results of the Unit tests
    - BUG/MINOR: cfgparse/peers: fix inconsistent check for missing peer server
    - BUG/MINOR: cfgparse/peers: properly handle ignored local peer case
    - BUG/MINOR: server: dont return immediately from parse_server() when skipping checks
    - MINOR: cfgparse/peers: provide more info when ignoring invalid "peer" or "server" lines
    - BUG/MINOR: stream: fix age calculation in "show sess" output
    - MINOR: stream/cli: rework "show sess" to better consider optional arguments
    - MINOR: stream/cli: make "show sess" support filtering on front/back/server
    - TESTS: quic: create first quic unittest
    - MINOR: h3/hq-interop: restore function for standalone FIN receive
    - MINOR/OPTIM: mux-quic: do not allocate rxbuf on standalone FIN
    - MINOR: mux-quic: refine reception of standalone STREAM FIN
    - MINOR: mux-quic: define globally stream rxbuf size
    - MINOR: mux-quic: define rxbuf wrapper
    - MINOR: mux-quic: store QCS Rx buf in a single-entry tree
    - MINOR: mux-quic: adjust Rx data consumption API
    - MINOR: mux-quic: adapt return value of qcc_decode_qcs()
    - MAJOR: mux-quic: support multiple QCS RX buffers
    - MEDIUM: mux-quic: handle too short data splitted on multiple rxbuf
    - MAJOR: mux-quic: increase stream flow-control for multi-buffer alloc
    - BUG/MINOR: cfgparse-tcp: relax namespace bind check
    - MINOR: startup: adjust alert messages, when capabilities are missed
2025-03-07 16:37:57 +01:00
Valentine Krasnobaeva
7d427134fe MINOR: startup: adjust alert messages, when capabilities are missed
CAP_SYS_ADMIN support was added, in order to access sockets in namespaces. So
let's adjust the alert at startup, where we check preserved capabilities from
global.last_checks. Let's mention here cap_sys_admin as well.
2025-03-07 16:37:16 +01:00
Damien Claisse
f0a07f834c BUG/MINOR: cfgparse-tcp: relax namespace bind check
Commit 5cbb278 introduced cap_sys_admin support, and enforced checks for
both binds and servers. However, when binding into a namespace, the bind
is done before dropping privileges. Hence, checking that we have
cap_sys_admin capability set in this case is not needed (and it would
decrease security to add it).
For users starting haproxy with other user than root and without
cap_sys_admin, bind should have already failed.
As a consequence, relax runtime check for binds into a namespace.
2025-03-07 16:23:29 +01:00
Amaury Denoyelle
dc7913d814 MAJOR: mux-quic: increase stream flow-control for multi-buffer alloc
Support for multiple Rx buffers per QCS instance has been introduced by
previous patches. However, due to flow-control initial values, client
were still unable to fully used this to increase their upload
throughput.

This patch increases max-stream-data-bidi-remote flow-control initial
values. A new define QMUX_STREAM_RX_BUF_FACTOR will fix the number of
concurrent buffers allocable per QCS. It is set to 90.

Note that connection flow-control initial value did not changed. It is
still configured to be equivalent to bufsize multiplied by the maximum
concurrent streams. This ensures that Rx buffers allocation is still
constrained per connection, so that it won't be possible to have all
active QCS instances using in parallel their maximum Rx buffers count.
2025-03-07 12:06:27 +01:00
Amaury Denoyelle
75027692a3 MEDIUM: mux-quic: handle too short data splitted on multiple rxbuf
Previous commit introduces support for multiple Rx buffers per QCS
instance. Contiguous data may be splitted accross multiple buffers
depending on their offset.

A particular issue could arise with this new model. Indeed, app_ops
rcv_buf callback can still deal with a single buffer at a time. This may
cause a deadlock in decoding if app_ops layer cannot proceed due to
partial data, but such data are precisely divided on two buffers. This
can for example intervene during HTTP/3 frame header parsing.

To deal with this, a new function is implemented to force data realign
between two contiguous buffers. This is called only when app_ops rcv_buf
returned 0 but data is available in the next buffer after the current
one. In this case, data are transferred from the next into the current
buffer via qcs_transfer_rx_data(). Decoding is then restarted, which
should ensure that app_ops layer has enough data to advance.

During this operation, special care is ensure to removed both
qc_stream_rxbuf entries, as their offset are adjusted. The next buffer
is only reinserted if there is remaining data in it, else it can be
freed.

This case is not easily reproducible as it depends on the HTTP/3 framing
used by the client. It seems to be easily reproduced though with quiche.
$ quiche-client --http-version HTTP/3 --method POST --body /tmp/100m \
  "https://127.0.0.1:20443/post"
2025-03-07 12:06:27 +01:00
Amaury Denoyelle
60f64449fb MAJOR: mux-quic: support multiple QCS RX buffers
Implement support for multiple Rx buffers per QCS instances. This
requires several changes mostly in qcc_recv() / qcc_decode_qcs() which
deal with STREAM frames reception and decoding. These multiple buffers
can be stored in QCS rx.bufs tree which was introduced in an earlier
patch.

On STREAM frame reception, a buffer is retrieved from QCS bufs tree, or
allocated if necessary, based on the data starting offset. Each buffers
are aligned on bufsize for convenience. This ensures there is no overlap
between two contiguous buffers. Special care is taken when dealing with
a STREAM frame which must be splitted and stored in two contiguous
buffers.

When decoding input data, qcc_decode_qcs() is still invoked with a
single buffer as input. This requires a new while loop to ensure
decoding is performed accross multiple contiguous buffers until all data
are decoded or app stream buffer is full.

Also, after qcs_consume() has been performed, the stream Rx channel is
immediately closed if FIN was already received and QCS now contains only
a single buffer with all remaining data. This is necessary as qcc_recv()
is unable to close the Rx channel if FIN is received for a buffer
different from the current readable offset.

Note that for now stream flow-control value is still too low to fully
utilizing this new infrastructure and improve clients upload throughput.
Indeed, flow-control max-stream-data initial values are set to match
bufsize. This ensures that each QCS will use 1 buffer, or at most 2 if
data are splitted. A future patch will increase this value to unblock
this limitation.
2025-03-07 12:06:26 +01:00
Amaury Denoyelle
7b168e356f MINOR: mux-quic: adapt return value of qcc_decode_qcs()
Change return value of qcc_decode_qcs(). It now directly returns the
value from app_ops rcv_buf callback. Function documentation is updated
to reflect this.

For now, qcc_decode_qcs() return value is ignored by callers, so this
patch should not have any functional change. However, it will become
necessary when implementing multiple Rx buffers per QCS, as a loop will
be implemented to invoke qcc_decode_qcs() on several contiguous buffers.
Decoding must be stopped however as soon as an error is returned by
rcv_buf callback. This is also the case in case of a null value, which
indicates there is not enough data to continue decoding.
2025-03-07 12:06:26 +01:00
Amaury Denoyelle
6b5607d66f MINOR: mux-quic: adjust Rx data consumption API
HTTP/3 data are converted into HTX via qcc_decode_qcs() function. On
completion, these data are removed from QCS Rx buffer via qcs_consume().

This patch adjust qcs_consume() API with several changes. Firstly, the
Rx buffer instance to operate on must now be specified as a new argument
to the function. Secondly, buffer liberation when all data were removed
from qcs_consume() is extracted up to qcc_decode_qcs() caller.

No functional change with this patch. The objective is to have an API
which can be better adapted to multiple Rx buffers per QCS instance.
2025-03-07 12:06:26 +01:00
Amaury Denoyelle
a4f31ffeeb MINOR: mux-quic: store QCS Rx buf in a single-entry tree
Convert QCS rx buffer pointer to a tree container. Additionnaly, offset
field of qc_stream_rxbuf is thus transformed into a node tree.

For now, only a single Rx buffer is stored at most in QCS tree. Multiple
Rx buffers will be implemented in a future patch to improve QUIC clients
upload throughput.
2025-03-07 12:06:26 +01:00
Amaury Denoyelle
cc3c2d1f12 MINOR: mux-quic: define rxbuf wrapper
Define a new type qc_stream_rxbuf. This is used as a wrapper around QCS
Rx buffer with encapsulation of the ncbuf storage. It is allocated via a
new pool. Several functions are adapted to be able to deal with
qc_stream_rxbuf as a wrapper instead of the previous plain ncbuf
instance.

No functional change should happen with this patch. For now, only a
single qc_stream_rxbuf can be instantiated per QCS. However, this new
type will be useful to implement multiple Rx buffer storage in a future
commit.
2025-03-07 12:06:26 +01:00
Amaury Denoyelle
4b1e63d191 MINOR: mux-quic: define globally stream rxbuf size
QCS uses ncbuf for STREAM data storage. This serves as a limit for
maximum STREAM buffering capacity, advertised via QUIC transport
parameters for initial flow-control values.

Define a new function qmux_stream_rx_bufsz() which can be used to
retrieve this Rx buffer size. This can be used both in MUX/H3 layers and
in QUIC transport parameters.
2025-03-07 12:06:26 +01:00
Amaury Denoyelle
7dd1eec2b1 MINOR: mux-quic: refine reception of standalone STREAM FIN
Reception of standalone STREAM FIN is a corner case, which may be
difficult to handle. In particular, care must be taken to ensure app_ops
rcv_buf() is always called to be notify about FIN, even if Rx buffer is
empty or full demux flag is set. If this is the case, it could prevent
closure of QCS Rx channel.

To ensure this, rcv_buf() was systematically called if FIN was received,
with or without data payload. This could called unnecessary invokation
when FIN is transmitted with data and full demux flag is set, or data
are received out-of-order.

This patches improve qcc_recv() by detecting explicitely a standalone
FIN case. Thus, rcv_buf() is only forcefully called in this case and if
all data were already previously received.
2025-03-07 12:06:26 +01:00
Amaury Denoyelle
20dc8e4ec2 MINOR/OPTIM: mux-quic: do not allocate rxbuf on standalone FIN
STREAM FIN may be received without any payload. However, qcc_recv()
always called qcs_get_ncbuf() indiscriminately, which may allocate a QCS
Rx buffer. This is unneeded as there is no payload to store.

Improve this by skipping qcs_get_ncbuf() invokation when dealing with a
standalone FIN signal. This should prevent superfluous buffer
allocation.
2025-03-07 12:06:26 +01:00
Amaury Denoyelle
861b11334c MINOR: h3/hq-interop: restore function for standalone FIN receive
Previously, a function qcs_http_handle_standalone_fin() was implemented
to handle a received standalone FIN, bypassing app_ops layer decoding.
However, this was removed as app_ops layer interaction is necessary. For
example, HTTP/3 checks that FIN is never sent on the control uni stream.

This patch reintroduces qcs_http_handle_standalone_fin(), albeit in a
slightly diminished version. Most importantly, it is now the
responsibility of the app_ops layer itself to use it, to avoid the
shortcoming described above.

The main objective of this patch is to be able to support standalone FIN
in HTTP/0.9 layer. This is easily done via the reintroduction of
qcs_http_handle_standalone_fin() usage. This will be useful to perform
testing, as standalone FIN is a corner case which can easily be broken.
2025-03-07 12:06:26 +01:00
Amaury Denoyelle
6f95d0dad0 TESTS: quic: create first quic unittest
Define a first unit-test dedicated to QUIC. A single test for now
ensures that variable length decoding is compliant. This should be
extended in the future with new set of tests.
2025-03-07 12:06:26 +01:00
Willy Tarreau
5e558c1727 MINOR: stream/cli: make "show sess" support filtering on front/back/server
With "show sess", particularly "show sess all", we're often missing the
ability to inspect only streams attached to a frontend, backend or server.
Let's just add these filters to the command. Only one at a time may be set.

One typical use case could be to dump streams attached to a server after
issuing "shutdown sessions server XXX" to figure why any wouldn't stop
for example.
2025-03-07 10:38:12 +01:00
Willy Tarreau
2bd7cf53cb MINOR: stream/cli: rework "show sess" to better consider optional arguments
The "show sess" CLI command parser is getting really annoying because
several options were added in an exclusive mode as the single possible
argument. Recently some cumulable options were added ("show-uri") but
the older ones were not yet adapted. Let's just make sure that the
various filters such as "older" and "age" now belong to the options
and leave only <id>, "all", and "help" for the first ones. The doc was
updated and it's now easier to find these options.
2025-03-07 10:36:58 +01:00
Willy Tarreau
1cdf2869f6 BUG/MINOR: stream: fix age calculation in "show sess" output
The "show sess" output reports an age that's based on the last byte of
the HTTP request instead of the stream creation date, due to a confusion
between logs->request_ts and the request_date sample fetch function. Most
of the time these are equal except when the request is not yet full for
any reason (e.g. wait-body). This explains why a few "show sess" could
report a few new streams aged by 99 days for example.

Let's perform the correct request timestamp calculation like the sample
fetch function does, by adding t_idle and t_handshake to the accept_ts.
Now the stream's age is correct and can be correctly used with the
"show sess older <age>" variant.

This issue was introduced in 2.9 and the fix can be backported to 3.0.
2025-03-07 10:36:58 +01:00
Aurelien DARRAGON
dbb25720dd MINOR: cfgparse/peers: provide more info when ignoring invalid "peer" or "server" lines
Invalid (incomplete) "server" or "peer" lines under peers section are now
properly ignored. For completeness, in this patch we add some reports so
that the user knows that incomplete lines were ignored.

For an incomplete server line, since it is tolerated (see GH #565), we
only emit a diag warning.

For an incomplete peer line, we report a real warning, as it is not
expected to have a peer line without an address:port specified.

Also, 'newpeer == curpeers->local' check could be simplified since
we already have the 'local_peer' variable which tells us that the
parsed line refers to a local peer.
2025-03-07 09:39:51 +01:00
Aurelien DARRAGON
a76b5358f0 BUG/MINOR: server: dont return immediately from parse_server() when skipping checks
If parse_server() is called under peers section parser, and the address
needs to be parsed but it is missing, we directly return from the function

However since 0fc136ce5b ("REORG: server: use parsing ctx for server
parsing"), parse_server() uses parsing ctx to emit warning/errors, and
the ctx must be reset before returning from the function, yet this early
return was overlooked. Because of that, any ha_{warning,alert..} message
reported after early return from parse_server() could cause messages to
have an extra "parsing [file:line]" info.

We fix that by ensuring parse_server() doesn't return without resetting
the parsing context.

It should be backported up to 2.6
2025-03-07 09:39:46 +01:00
Aurelien DARRAGON
054443dfb9 BUG/MINOR: cfgparse/peers: properly handle ignored local peer case
In 8ba10fea6 ("BUG/MINOR: peers: Incomplete peers sections should be
validated."), some checks were relaxed in parse_server(), and extra logic
was added in the peers section parser in an attempt to properly ignore
incomplete "server" or "peer" statement under peers section.

This was done in response to GH #565, the main intent was that haproxy
should already complain about incomplete peers section (ie: missing
localpeer).

However, 8ba10fea69 explicitly skipped the peer cleanup upon missing
srv association for local peers. This is wrong because later haproxy
code always assumes that peer->srv is valid. Indeed, we got reports
that the (invalid) config below would cause segmentation fault on
all stable versions:

 global
   localpeer 01JM0TEPAREK01FQQ439DDZXD8

 peers my-table
   peer 01JM0TEPAREK01FQQ439DDZXD8

 listen dummy
   bind localhost:8080

To fix the issue, instead of by-passing some cleanup for the local
peer, handle this case specifically by doing the regular peer cleanup
and reset some fields set on the curpeers and curpeers proxy because
of the invalid local peer (do as if the peer was not declared).

It should still comply with requirements from #565.

This patch should be backported to all stable versions.
2025-03-06 22:05:29 +01:00
Aurelien DARRAGON
2560ab892f BUG/MINOR: cfgparse/peers: fix inconsistent check for missing peer server
In the "peers" section parser, right after parse_server() is called, we
used to check whether the curpeers->peers_fe->srv pointer was set or not
to know if parse_server() successfuly added a server to the peers proxy,
server that we can then associate to the new peer.

However the check is wrong, as curpeers->peers_fe->srv points to the
last added server, if a server was successfully added before the
failing one, we cannot detect that the last parse_server() didn't
add a server. This is known to cause bug with bad "peer"/"server"
statements.

To fix the issue, we save a pointer on the last known
curpeers->peers_fe->srv before parse_server() is called, and we then
compare the save with the pointer after parse_server(), if the value
didn't change, then parse_server() didn't add a server. This makes
the check consistent in all situations.

It should be backported to all stable versions.
2025-03-06 22:05:24 +01:00
William Lallemand
29db5406b4 CI: github: show results of the Unit tests
Add a "Show Unit-Tests results" section which show each unit test which
failed by displaying their result file.
2025-03-06 21:23:54 +01:00
William Lallemand
0b22c8e0e0 TESTS: unit-tests: store sh -x in a result file
Store `sh -e -x` of the test in a result file. This file is deleted upon
success, but can be consulted if the test fails
2025-03-06 21:22:38 +01:00
William Lallemand
7fdc4160b2 TESTS: change the output of run-unittests.sh
- "check" is run with sh -e so it will stop at the first error
- output of "check" is not shown anymore
- add a line with the name of the failed test
2025-03-06 17:53:53 +01:00
Valentine Krasnobaeva
e900ef987e BUG/MEIDUM: startup: return to initial cwd only after check_config_validity()
In check_config_validity() we evaluate some sample fetch expressions
(log-format, server rules, etc). These expressions may use external files like
maps.

If some particular 'default-path' was set in the global section before, it's no
longer applied to resolve file pathes in check_config_validity(). parse_cfg()
at the end of config parsing switches back to the initial cwd.

This fixes the issue #2886.

This patch should be backported in all stable versions since 2.4.0, including
2.4.0.
2025-03-06 10:49:48 +01:00
Roberto Moreda
f98b5c4f59 MINOR: log: add dont-parse-log and assume-rfc6587-ntf options
This commit introduces the dont-parse-log option to disable log message
parsing, allowing raw log data to be forwarded without modification.

Also, it adds the assume-rfc6587-ntf option to frame log messages
using only non-transparent framing as per RFC 6587. This avoids
missparsing in certain cases (mainly with non RFC compliant messages).

The documentation is updated to include details on the new options and
their intended use cases.

This feature was discussed in GH #2856
2025-03-06 09:30:39 +01:00
Roberto Moreda
c25e6f5efa MINOR: log: detach prepare from parse message
This commit adds a new function `prepare_log_message` to initialize log
message buffers and metadata. This function sets default values for log
level and facility, ensuring a consistent starting state for log
processing. It also prepares the buffer and metadata fields, simplifying
subsequent log parsing and construction.
2025-03-06 09:30:31 +01:00
Roberto Moreda
834e9af877 MINOR: log: add options eval for log-forward
This commit adds parsing of options in log-forward config sections and
prepares the scenario to implement actual changes of behaviuor. So far
we only take in account proxy->options2, which is the bit container with
more available positions.
2025-03-06 09:30:25 +01:00
Aurelien DARRAGON
0746f6bde0 MINOR: cfgparse-listen: add and use cfg_parse_listen_match_option() helper
cfg_parse_listen_match_option() takes cfg_opt array as parameter, as well
current args, expected mode and cap bitfields.

It is expected to be used under cfg_parse_listen() function or similar.
Its goal is to remove code duplication around proxy->options and
proxy->options2 handling, since the same checks are performed for the
two. Also, this function could help to evaluate proxy options for
mode-specific proxies such as log-forward section for instance:
by giving the expected mode and capatiblity as input, the function
would only match compatible options.
2025-03-06 09:30:18 +01:00
Aurelien DARRAGON
d9aa199100 MINOR: proxy: make pr_mode enum bitfield compatible
Current pr_mode enum is a regular enum because a proxy only supports one
mode at a time. However it can be handy for a function to be given a
list of compatible modes for a proxy, and we can't do that using a
bitfield because pr_mode is not bitfield compatible (values share
the same bits).

In this patch we manually define pr_mode values so that they are all
using separate bits and allows a function to take a bitfield of
compatible modes as parameter.
2025-03-06 09:30:11 +01:00
Aurelien DARRAGON
c7abe7778e MEDIUM: log: postpone the decision to send or not log with empty messages
As reported by Nick Ramirez in GH #2891, it is currently not possible to
use log-profile without a log-format set on the proxy.

This is due to historical reason, because all log sending functions avoid
trying to send a log with empty message. But now with log-profile which
can override log-format, it is possible that some loggers may actually
end up generating a valid log message that should be sent! Yet from the
upper logging functions we don't know about that because loggers are
evaluated in lower API functions.

Thus, to avoid skipping potentially valid messages (thanks to log-profile
overrides), in this patch we postpone the decision to send or not empty
log messages in lower log API layer, ie: _process_send_log_final(), once
the log-profile settings were evaluated for a given logger.

A known side-effect of this change is that fe->log_count statistic may
be increased even if no log message is sent because the message was empty
and even the log-profile didn't help to produce a non empty log message.
But since configurations lacking proxy log-format are not supposed to be
used without log-profile (+ log steps combination) anyway it shouldn't be
an issue.
2025-03-05 15:38:52 +01:00
Aurelien DARRAGON
9e9b110032 MINOR: log: use __send_log() with exact payload length
Historically, __send_log() was called with terminating NULL byte after
the message payload. But now that __send_log() supports being called
without terminating NULL byte (thanks to size hint), and that __sendlog()
actually stips any \n or NULL byte, we don't need to bother with that
anymore. So let's remove extra logic around __send_log() users where we
added 1 extra byte for the terminating NULL byte.

No change of behavior should be expected.
2025-03-05 15:38:46 +01:00
Aurelien DARRAGON
94a9b0f5de BUG/MINOR: log: set proper smp size for balance log-hash
result.data.u.str.size was set to size+1 to take into account terminating
NULL byte as per the comment. But this is wrong because the caller is free
to set size to just the right amount of bytes (without terminating NULL
byte). In fact all smp API functions will not read past str.data so there
is not risk about uninitialized reads, but this leaves an ambiguity for
converters that may use all the smp size to perform transformations, and
since we don't know about the "message" memory origin, we cannot assume
that its size may be greater than size. So we max it out to size just to
be safe.

This bug was not known to cause any issue, it was spotted during code
review. It should be backported in 2.9 with b30bd7a ("MEDIUM: log/balance:
support for the "hash" lb algorithm")
2025-03-05 15:38:41 +01:00
Aurelien DARRAGON
ddf66132f4 CLEANUP: log: removing "log-balance" references
This is a complementary patch to 0e1f389fe9 ("DOC: config: removing
"log-balance" references"): we properly removed all log-balance
references in the doc but there remained some in the code, let's fix
that.

It could be backported in 2.9 with 0e1f389fe9
2025-03-05 15:38:34 +01:00
Valentine Krasnobaeva
b46b81949f MINOR: sample: allow custom date format in error-log-format
Sample fetches %[accept_date] and %[request_date] with converters can be used
in error-log-format string. But in the most error cases they fetches nothing,
as error logs are produced on SSL handshake issues or when invalid PROXY
protocol header is used. Stream object is never allocated in such cases and
smp_fetch_accept_date() just simply returns 0.

There is a need to have a custom date format (ISO8601) also in the error logs,
along with normal logs. When sess_build_logline_orig() builds log line it
always copies the accept date to strm_logs structure. When stream is absent,
accept date is copied from the session object.

So, if the steam object wasn't allocated, let's use the session date info in
smp_fetch_accept_date(). This allows then, in sample_process(), to apply to the
fetched date different converters and formats.

This fixes the issue #2884.
2025-03-04 18:57:29 +01:00
Olivier Houchard
335ef3264b DEBUG: init: Add a macro to register unit tests
Add a new macro, REGISTER_UNITTEST(), that will automatically make sure
we call hap_register_unittest(), instead of having to create a function
that will do so.
2025-03-04 18:18:10 +01:00
William Lallemand
588237ca6e CI: github: fix h2spec.config proxy names
h2spec.config config file emitted a warning because the frontend name
has the same name as the backend.
2025-03-04 11:44:03 +01:00
William Lallemand
06d86822c1 TESTS: ist: add a ist.sh to launch in make unit-tests
Compile and run the ist unit tests from ist.sh
2025-03-04 11:25:35 +01:00
William Lallemand
11ea331e20 TESTS: ist: use the exit code to return a verdict
Use the exit code to return a verdict on the test.
2025-03-04 11:25:35 +01:00
William Lallemand
ddd2c82a35 TESTS: ist: fix wrong array size
test_istzero() and test_istpad() has the wrong array size buf[] which
lacks the space for the '\0';

Could be backported in every stable branches.
2025-03-04 11:25:25 +01:00
William Lallemand
937ece45d4 CI: github: remove smoke tests from vtest.yml
Smoke tests from the vtest.yml are not useful anymore since they are run
directly by tests/unit/smoke/test.sh. This patch removes them.
2025-03-03 12:46:20 +01:00
William Lallemand
cf71e9f5cf MINOR: jws: conversion to NIST curves name
OpenSSL version greater than 3.0 does not use the same API when
manipulating EVP_PKEY structures, the EC_KEY API is deprecated and it's
not possible anymore to get an EC_GROUP and simply call
EC_GROUP_get_curve_name().

Instead, one must call EVP_PKEY_get_utf8_string_param with the
OSSL_PKEY_PARAM_GROUP_NAME parameter, but this would result in a SECG
curves name, instead of a NIST curves name in previous version.
(ex: secp384r1 vs P-384)

This patch adds 2 functions:

- the first one look for a curves name and converts it to an openssl
  NID.

- the second one converts a NID to a NIST curves name

The list only contains: P-256, P-384 and P-521 for now, it could be
extended in the fure with more curves.
2025-03-03 12:43:32 +01:00
William Lallemand
8a6b0b06cd TESTS: add config smoke checks in the unit tests
vtest.yml contains some config checks that are used to check the
memleaks.

This patch adds a unit test which runs the same tests.
2025-03-03 12:43:32 +01:00
William Lallemand
7a2a613132 CI: github: run make unit-tests on the CI
Run the new make unit-tests on the CI.

It requires HAProxy to be built with -DDEBUG_UNIT so the -U option is
available in HAProxy
2025-03-03 12:43:32 +01:00
William Lallemand
09457111bb TESTS: jws: register a unittest for jwk
Add a way to test the jwk converter in the unit test system

    $ make TARGET=linux-glibc USE_OPENSSL=1 CFLAGS="-DDEBUG_UNIT=1"
    $ ./haproxy -U jwk foobar.pem.rsa
    {
        "kty": "RSA",
        "n":   "...",
        "e":   "AQAB"
    }
    $ ./haproxy -U jwk foobar.pem.ecdsa
    {
        "kty": "EC",
        "crv": "P-384",
        "x":   "...",
        "y":   "..."
    }

This is then tested by a shell script:

    $ HAPROXY_PROGRAM=${PWD}/haproxy tests/unit/jwk/test.sh
    + readlink -f tests/unit/jwk/test.sh
    + BASENAME=/haproxy/tests/unit/jwk/test.sh
    + dirname /haproxy/tests/unit/jwk/test.sh
    + TESTDIR=/haproxy/tests/unit/jwk
    + HAPROXY_PROGRAM=/haproxy/haproxy
    + mktemp
    + FILE1=/tmp/tmp.iEICxC5yNK
    + /haproxy/haproxy -U jwk /haproxy/tests/unit/jwk/ecdsa.key
    + diff -Naurp /haproxy/tests/unit/jwk/ecdsa.pub.jwk /tmp/tmp.iEICxC5yNK
    + rm /tmp/tmp.iEICxC5yNK
    + mktemp
    + FILE2=/tmp/tmp.EIrGZGaCDi
    + /haproxy/haproxy -U jwk /haproxy/tests/unit/jwk/rsa.key
    + diff -Naurp /haproxy/tests/unit/jwk/rsa.pub.jwk /tmp/tmp.EIrGZGaCDi
    + rm /tmp/tmp.EIrGZGaCDi

    $ echo $?
    0
2025-03-03 12:43:32 +01:00
William Lallemand
1e7478bb4e TESTS: add a unit test runner in the Makefile
`make unit-tests` would run shell scripts from tests/unit/

The run-unittests.sh script will look for any .sh in tests/unit/ and
will call it twice:

- first with the 'check' argument in order to decide if we should skip
  the test or not
- second to run the check

A simple test could be written this way:

	#!/bin/sh

	check() {
	       ${HAPROXY_PROGRAM} -cc 'feature(OPENSSL)'
	       command -v socat
	}

	run() {
		 ${HAPROXY_PROGRAM} -dI -f ${ROOTDIR}/examples/quick-test.cfg -c
	}

	case "$1" in
	       "check")
	               check
	       ;;
	       "run")
	               run
	       ;;
	esac

The tests *MUST* be written in POSIX shell in order to be portable, and
any special commands should be tested with `command -v` before using it.

Tests are run with `sh -e` so everything must be tested.
2025-03-03 12:43:32 +01:00
William Lallemand
a647839954 DEBUG: init: add a way to register functions for unit tests
Doing unit tests with haproxy was always a bit difficult, some of the
function you want to test would depend on the buffer or trash buffer
initialisation of HAProxy, so building a separate main() for them is
quite hard.

This patch adds a way to register a function that can be called with the
"-U" parameter on the command line, will be executed just after
step_init_1() and will exit the process with its return value as an exit
code.

When using the -U option, every keywords after this option is passed to
the callback and could be used as a parameter, letting the capability to
handle complex arguments if required by the test.

HAProxy need to be built with DEBUG_UNIT to activate this feature.
2025-03-03 12:43:32 +01:00
William Lallemand
4dc0ba233e MINOR: jws: implement a JWK public key converter
Implement a converter which takes an EVP_PKEY and converts it to a
public JWK key. This is the first step of the JWS implementation.

It supports both EC and RSA keys.

Know to work with:

- LibreSSL
- AWS-LC
- OpenSSL > 1.1.1
2025-03-03 12:43:32 +01:00
Willy Tarreau
730641f7ca BUG/MINOR: server: check for either proxy-protocol v1 or v2 to send hedaer
As reported in issue #2882, using "no-send-proxy-v2" on a server line does
not properly disable the use of proxy-protocol if it was enabled in a
default-server directive in combination with other PP options. The reason
for this is that the sending of a proxy header is determined by a test on
srv->pp_opts without any distinction, so disabling PPv2 while leaving other
options results in a PPv1 header to be sent.

Let's fix this by explicitly testing for the presence of either send-proxy
or send-proxy-v2 when deciding to send a proxy header.

This can be backported to all versions. Thanks to Andre Sencioles (@asenci)
for reporting the issue and testing the fix.
2025-03-03 04:05:47 +01:00
Amaury Denoyelle
d0f97040a3 BUG/MINOR: hq-interop: fix leak in case of rcv_buf early return
HTTP/0.9 parser was recently updated to support truncated requests in
rcv_buf operation. However, this caused a leak as input buffer is
allocated early.

In fact, the leak was already present in case of fatal errors. Fix this
by first delaying buffer allocation, so that initial checks are
performed before. Then, ensure that buffer is released in case of a
latter error.

This is considered as minor, as HTTP/0.9 is reserved for experiment and
QUIC interop usages.

This should be backported up to 2.6.
2025-02-28 17:37:00 +01:00
Willy Tarreau
fd5d59967a MINOR: h1: permit to relax the websocket checks for missing mandatory headers
At least one user would like to allow a standards-violating client setup
WebSocket connections through haproxy to a standards-violating server that
accepts them. While this should of course never be done over the internet,
it can make sense in the datacenter between application components which do
not need to mask the data, so this typically falls into the situation of
what the "accept-unsafe-violations-in-http-request" option and the
"accept-unsafe-violations-in-http-response" option are made for.
See GH #2876 for more context.

This patch relaxes the test on the "Sec-Websocket-Key" header field in
the request, and of the "Sec-Websocket-Accept" header in the response
when these respective options are set.

The doc was updated to reference this addition. This may be backported
to 3.1 but preferably not further.
2025-02-28 17:31:20 +01:00
Christopher Faulet
0e08252294 BUG/MEDIUM: mux-fcgi: Try to fully fill demux buffer on receive if not empty
Don't reserve space for the HTX overhead on receive if the demux buffer is
not empty. Otherwise, the demux buffer may be erroneously reported as full
and this may block records processing. Because of this bug, a ping-pong loop
till timeout between data reception and demux process can be observed.

This bug was introduced by the commit 5f927f603 ("BUG/MEDIUM: mux-fcgi:
Properly handle read0 on partial records"). To fix the issue, if the demux
buffer is not empty when we try to receive more data, all free space in the
buffer can now be used. However, if the demux buffer is empty, we still try
to keep it aligned with the HTX.

This patch must be backported to 3.1.
2025-02-28 16:07:05 +01:00
Amaury Denoyelle
3cc095a011 MINOR: hq-interop: properly handle incomplete request
Extends HTTP/0.9 layer to be able to deal with incomplete requests.
Instead of an error, 0 is returned. Thus, instead of a stream closure.
QUIC-MUX may retry rcv_buf operation later if more data is received,
similarly to HTTP/3 layer.

Note that HTTP/0.9 is only used for testing and interop purpose. As
such, this limitation is not considered as a bug. It is probably not
worth to backport it.
2025-02-27 17:34:06 +01:00
Amaury Denoyelle
0aa35289b3 CLEANUP: h3: fix documentation of h3_rcv_buf()
Return value of h3_rcv_buf() is incorrectly documented. Indeed, it may
return a positive value to indicate that input bytes were converted into
HTX. This is especially important, as caller uses this value to consume
the reported data amount in QCS Rx buffer.

This should be backported up to 2.6. Note that on 2.8, h3_rcv_buf() was
named h3_decode_qcs().
2025-02-27 17:31:40 +01:00
Amaury Denoyelle
f6648d478b BUG/MINOR: h3: do not report transfer as aborted on preemptive response
HTTP/3 specification allows a server to emit the entire response even if
only a partial request was received. In particular, this happens when
request STREAM FIN is delayed and transmitted in an empty payload frame.

In this case, qcc_abort_stream_read() was used by HTTP/3 layer to emit a
STOP_SENDING. Remaining received data were not transmitted to the stream
layer as they were simply discared. However, this prevents FIN
transmission to the stream layer. This causes the transfer to be
considered as prematurely closed, resulting in a cL-- log line status.
This is misleading to users which could interpret it as if the response
was not sent.

To fix this, disable STOP_SENDING emission on full preemptive reponse
emission. Rx channel is kept opened until the client closes it with
either a FIN or a RESET_STREAM. This ensures that the FIN signal can be
relayed to the stream layer, which allows the transfer to be reported as
completed.

This should be backported up to 2.9.
2025-02-27 17:23:24 +01:00
Dragan Dosen
0ae7a5d672 BUG/MINOR: server: fix the "server-template" prefix memory leak
The srv->tmpl_info.prefix was not freed in srv_free_params().

This could be backported to all stable versions.
2025-02-27 04:21:01 +01:00
Dragan Dosen
6838fe43a3 BUG/MEDIUM: server: properly initialize PROXY v2 TLVs
The PROXY v2 TLVs were not properly initialized when defined with
"set-proxy-v2-tlv-fmt" keyword, which could have caused a crash when
validating the configuration or malfunction (e.g. when used in
combination with "server-template" and/or "default-server").

The issue was introduced with commit 6f4bfed3a ("MINOR: server: Add
parser support for set-proxy-v2-tlv-fmt").

This should be backported up to 2.9.
2025-02-27 04:20:45 +01:00
Olivier Houchard
706b008429 MEDIUM: servers: Add strict-maxconn.
Maxconn is a bit of a misnomer when it comes to servers, as it doesn't
control the maximum number of connections we establish to a server, but
the maximum number of simultaneous requests. So add "strict-maxconn",
that will make it so we will never establish more connections than
maxconn.
It extends the meaning of the "restricted" setting of
tune.takeover-other-tg-connections, as it will also attempt to get idle
connections from other thread groups if strict-maxconn is set.
2025-02-26 13:00:18 +01:00
Olivier Houchard
8de8ed4f48 MEDIUM: connections: Allow taking over connections from other tgroups.
Allow haproxy to take over idle connections from other thread groups
than our own. To control that, add a new tunable,
tune.takeover-other-tg-connections. It can have 3 values, "none", where
we won't attempt to get connections from the other thread group (the
default), "restricted", where we only will try to get idle connections
from other thread groups when we're using reverse HTTP, and "full",
where we always try to get connections from other thread groups.
Unless there is a special need, it is advised to use "none" (or
restricted if we're using reverse HTTP) as using connections from other
thread groups may have a performance impact.
2025-02-26 13:00:18 +01:00
Olivier Houchard
d31b1650ae MEDIUM: pollers: Drop fd events after a takeover to another tgid.
In pollers that support it, provide the generation number in addition to
the fd, and, when an event happened, if the generation number is the
same, but the tgid changed, then assumed the fd was taken over by a
thread from another thread group, and just delete the event from the
current thread's poller, as we no longer want to hear about it.
2025-02-26 13:00:18 +01:00
Olivier Houchard
c36aae2af1 MINOR: pollers: Add a fixup_tgid_takeover() method.
Add a fixup_tgid_takeover() method to pollers for which it makes sense
(epoll, kqueue and evport). That method can be called after a takeover
of a fd from a different thread group, to make sure the poller's
internal structure reflects the new state.
2025-02-26 13:00:18 +01:00
Olivier Houchard
752c5cba5d MEDIUM: epoll: Make sure we can add a new event
Check that the call to epoll_ctl() succeeds, and if it does not, if
we're adding a new event and it fails with EEXIST, then delete and
re-add the event. There are a few cases where we may already have events
for a fd. If epoll_ctl() fails for any reason, use BUG_ON to make sure
we immediately crash, as this should not happen.
2025-02-26 13:00:18 +01:00
Olivier Houchard
c5cc09c00d MINOR: fd: Add fd_lock_tgid_cur().
Add fd_lock_tgid_cur(), a function that will lock the tgid, without
modifying its value.
2025-02-26 13:00:18 +01:00
Olivier Houchard
52b97ff8dd MEDIUM: fd: Wait if locked in fd_grab_tgid() and fd_take_tgid().
Wait while the tgid is locked in fd_grab_tgid() and fd_take_tgid().
As that lock is barely used, it should have no impact.
2025-02-26 13:00:18 +01:00
Ilia Shipitsin
814b5dfe30 BUILD: add possibility to use different QuicTLS variants
initially QuicTLS started as a patchset on top of OpenSSL,
currently project has started its own journey as QuicTLS

somehow we need both

ML: https://www.mail-archive.com/haproxy@formilux.org/msg45574.html
GH: https://github.com/quictls/quictls/issues/244
2025-02-25 10:29:46 +01:00
Willy Tarreau
a826250659 OPTIM: connection: don't try to kill other threads' connection when !shared
Users may have good reasons for using "tune.idle-pool.shared off", one of
them being the cost of moving cache lines between cores, or the kernel-
side locking associated with moving FDs. For this reason, when getting
close to the file descriptors limits, we must not try to kill adjacent
threads' FDs when the sharing of pools is disabled. This is extremely
expensive and kills the performance. We must limit ourselves to our local
FDs only. In such cases, it's up to the users to configure a large enough
maxconn for their usages.

Before this patch, perf top reported 9% CPU usage in connect_server()
onthe trylock used to kill connections when running at 4800 conns for
a global maxconn of 6400 on a 128-thread server. Now it doesn't spend
its time there anymore, and performance has increased by 12%. Note,
it was verified that disabling the locks in such a case has no effect
at all, so better keep them and stay safe.
2025-02-25 09:23:46 +01:00
Willy Tarreau
2e0bac90da BUG/MEDIUM: stream: don't use localtime in dumps from a signal handler
In issue #2861, Jarosaw Rzesztko reported another issue with
"show threads", this time in relation with the conversion of a stream's
accept date to local time. Indeed, if the libc was interrupted in this
same function, it could have been interrupted with a lock held, then
it's no longer possible to dump the date, and we face a deadlock.
This is easy to reproduce with logging enabled.

Let's detect we come from a signal handler and do not try to resolve
the time to localtime in this case.
2025-02-24 13:40:42 +01:00
Willy Tarreau
fb7874c286 MINOR: tinfo: split the signal handler report flags into 3
While signals are not recursive, one signal (e.g. wdt) may interrupt
another one (e.g. debug). The problem this causes is that when leaving
the inner handler, it removes the outer's flag, hence the protection
that comes with it. Let's just have 3 distinct flags for regular signals,
debug signal and watchdog signal. We add a 4th definition which is an
aggregate of the 3 to ease testing.
2025-02-24 13:37:52 +01:00
Willy Tarreau
bbf824933f BUG/MINOR: h2: always trim leading and trailing LWS in header values
Annika Wickert reported some occasional disconnections between haproxy
and varnish when communicating over HTTP/2, with varnish complaining
about protocol errors while captures looked apparently normal. Nils
Goroll managed to reproduce this on varnish by injecting the capture of
the outgoing haproxy traffic and noticed that haproxy was forwarding a
header value containing a trailing space, which is now explicitly
forbidden since RFC9113.

It turns out that the only way for such a header to pass through haproxy
is to arrive in h2 and not be edited, in which case it will arrive in
HTX with its undesired spaces. Since the code dealing with HTX headers
always trims spaces around them, these are not observable in dumps, but
only when started in debug mode (-d). Conversions to/from h1 also drop
the spaces.

With this patch we trim LWS both on input and on output. This way we
always present clean headers in the whole stack, and even if some are
manually crafted by the configuration or Lua, they will be trimmed on
the output.

This must be backported to all stable versions.

Thanks to Annika for the helpful capture and Nils for the help with
the analysis on the varnish side!
2025-02-24 09:39:57 +01:00
Vincent Dechenaux
9011b3621b MINOR: compression: Introduce minimum size
This is the introduction of "minsize-req" and "minsize-res".
These two options allow you to set the minimum payload size required for
compression to be applied.
This helps save CPU on both server and client sides when the payload does
not need to be compressed.
2025-02-22 11:32:40 +01:00
Willy Tarreau
e7510d6230 CLEANUP: task: move the barrier after clearing th_ctx->current
There's a barrier after releasing the current task in the scheduler.
However it's improperly placed, it's done after pool_free() while in
fact it must be done immediately after resetting the current pointer.
Indeed, the purpose is to make sure that nobody sees the task as valid
when it's in the process of being released. This is something that
could theoretically happen if interrupted by a signal in the inlined
code of pool_free() if the compiler decided to postpone the write to
->current. In practice since nothing fancy is done in the inlined part
of the function, there's currently no risk of reordering. But it could
happen if the underlying __pool_free() were to be inlined for example,
and in this case we could possibly observe th_ctx->current pointing
to something currently being destroyed.

With the barrier between the two, there's no risk anymore.
2025-02-21 18:31:46 +01:00
Willy Tarreau
eb41d768f9 MINOR: tools: use only opportunistic symbols resolution
As seen in issue #2861, dladdr_and_size() an be quite expensive and
will often hold a mutex in the underlying library. It becomes a real
problem when issuing lots of "show threads" or wdt warnings in parallel
because threads will queue up waiting for each other to finish, adding
to their existing latency that possibly caused the warning in the first
place.

Here we're taking a different approach. If the thread is not isolated
and not panicking, it's doing unimportant stuff like showing threads
or warnings. In this case we try to grab a lock, and if we fail because
another thread is already there, we just pretend we cannot resolve the
symbol. This is not critical because then we fall back to the already
used case which consists in writing "main+<offset>". In practice this
will almost never happen except in bad situations which could have
otherwise degenerated.
2025-02-21 18:26:29 +01:00
Willy Tarreau
3c22fa315b BUG/MEDIUM: stream: use non-blocking freq_ctr calls from the stream dumper
The stream dump function is called from signal handlers (warning, show
threads, panic). It makes use of read_freq_ctr() which might possibly
block if it tries to access a locked freq_ctr in the process of being
updated, e.g. by the current thread.

Here we're relying on the non-blocking API instead. It may return incorrect
values (typically smaller ones after resetting the curr counter) but at
least it will not block.

This needs to be backported to stable versions along with the previous
commit below:

   MINOR: freq_ctr: provide non-blocking read functions

At least 3.1 is concerned as the warnings tend to increase the risk of
this situation appearing.
2025-02-21 18:26:29 +01:00
Willy Tarreau
29e246a84c MINOR: freq_ctr: provide non-blocking read functions
Some code called by the debug handlers in the context of a signal handler
accesses to some freq_ctr and occasionally ends up on a locked one from
the same thread that is dumping it. Let's introduce a non-blocking version
that at least allows to return even if the value is in the process of being
updated, it's less problematic than hanging.
2025-02-21 18:26:29 +01:00
Willy Tarreau
84d4c948fc BUG/MEDIUM: stream: never allocate connection addresses from signal handler
In __strm_dump_to_buffer(), we call conn_get_src()/conn_get_dst() to try
to retrieve the connection's IP addresses. But this function may be called
from a signal handler to dump a currently running stream, and if the
addresses were not allocated yet, a poll_alloc() will be performed while
we might possibly already be running pools code, resulting in pool list
corruption.

Let's just make sure we don't call these sensitive functions there when
called from a signal handler.

This must be backported at least to 3.1 and ideally all other versions,
along with this previous commit:

  MINOR: tinfo: add a new thread flag to indicate a call from a sig handler
2025-02-21 17:41:38 +01:00
Willy Tarreau
ddd173355c MINOR: tinfo: add a new thread flag to indicate a call from a sig handler
Signal handlers must absolutely not change anything, but some long and
complex call chains may look innocuous at first glance, yet result in
some subtle write accesses (e.g. pools) that can conflict with a running
thread being interrupted.

Let's add a new thread flag TH_FL_IN_SIG_HANDLER that is only set when
entering a signal handler and cleared when leaving them. Note, we're
speaking about real signal handlers (synchronous ones), not deferred
ones. This will allow some sensitive call places to act differently
when detecting such a condition, and possibly even to place a few new
BUG_ON().
2025-02-21 17:41:38 +01:00
Willy Tarreau
a56dfbdcb4 BUG/MINOR: mux-h1: always make sure h1s->sd exists in h1_dump_h1s_info()
This function may be called from a signal handler during a warning,
a panic or a show thread. We need to be more cautious about what may
or may not be dereferenced since an h1s is not necessarily fully
initialized. Loops of "show threads" sometimes manage to crash when
dereferencing a null h1s->sd, so let's guard it and add a comment
remining about the unusual call place.

This can be backported to the relevant versions.
2025-02-21 17:41:38 +01:00
Willy Tarreau
9d5bd47634 BUG/MINOR: stream: do not call co_data() from __strm_dump_to_buffer()
co_data() was instrumented to detect cases where c->output > data and
emits a warning if that's not correct. The problem is that it happens
quite a bit during "show threads" if it interrupts traffic anywhere,
and that in some environments building with -DDEBUG_STRICT_ACTION=3,
it will kill the process.

Let's just open-code the channel functions that make access to co_data(),
there are not that many and the operations remain very simple.

This can be backported to 3.1. It didn't trigger in earlier versions
because they didn't have this CHECK_IF_HOT() test.
2025-02-21 17:18:00 +01:00
Ilia Shipitsin
0bdf414fa5 CI: QUIC Interop: clean old docker images
currently temporary docker images are kept forever. let's delete
outdated ones
2025-02-21 11:34:43 +01:00
Aurelien DARRAGON
97a19517ff MINOR: clock: always use atomic ops for global_now_ms
global_now_ms is shared between threads so we must give hint to the
compiler that read/writes operations should be performed atomically.

Everywhere global_now_ms was used, atomic ops were used, except in
clock_update_global_date() where a read was performed without using
atomic op. In practise it is not an issue because on most systems
such reads should be atomic already, but to prevent any confusion or
potential bug on exotic systems, let's use an explicit _HA_ATOMIC_LOAD
there.

This may be backported up to 2.8
2025-02-21 11:22:35 +01:00
Aurelien DARRAGON
9561b9fb69 BUG/MINOR: sink: add tempo between 2 connection attempts for sft servers
When the connection for sink_forward_{oc}_applet fails or a previous one
is destroyed, the sft->appctx is instantly released.

However process_sink_forward_task(), which may run at any time, iterates
over all known sfts and tries to create sessions for orphan ones.

It means that instantly after sft->appctx is destroyed, a new one will
be created, thus a new connection attempt will be made.

It can be an issue with tcp log-servers or sink servers, because if the
server is unavailable, process_sink_forward() will keep looping without
any temporisation until the applet survives (ie: connection succeeds),
which results in unexpected CPU usage on the threads responsible for
that task.

Instead, we add a tempo logic so that a delay of 1second is applied
between two retries. Of course the initial attempt is not delayed.

This could be backported to all stable versions.
2025-02-21 11:22:35 +01:00
Aurelien DARRAGON
c9d4192726 BUG/MINOR: log: fix outgoing abns address family
While reviewing the code in an attempt to fix GH #2875, I stumbled
on another case similar to aac570c ("BUG/MEDIUM: uxst: fix outgoing
abns address family in connect()") that caused abns(z) addresses to
fail when used as log targets.

The underlying cause is the same as aac570c, which is the rework of the
unix socket families in order to support custom addresses for different
adressing schemes, where a real_family() was overlooked before passing
a haproxy-internal address struct to socket-oriented syscall.

To fix the issue, we first copy the target's addr, and then leverage
real_family() to set the proper low-level address family that is passed
to sendmsg() syscall.

It should be backported in 3.1
2025-02-21 11:22:28 +01:00
Aurelien DARRAGON
26d97ec148 REGTESTS: fix reg-tests/server/abnsz.vtc
It was proved in GH #2875 that the regtest was broken, at least for the
server-side abnsz, as the connect() was not performed using the proper
family, which results in kernel refusing to perform the call, while the
reg-test actually succeeds.

Indeed, in the test we used vtest client to connect to haproxy, which
then routed the request to another haproxy instance listening on an
abnsz socket, and this last haproxy was the one to answer the http
request.

As we only used "rxresp" in vtest client, the test succeeded with empty
responses, which was the case due to the server connection failing on the
first haproxy process.
2025-02-21 08:22:25 +01:00
Willy Tarreau
aac570cd03 BUG/MEDIUM: uxst: fix outgoing abns address family in connect()
Since we reworked the unix socket families in order to support custom
addresses for different addressing schemes, we've been using extra
values for the ss_family field in sockaddr_storage. These ones have
to be adjusted before calling bind() or connect(). It turns out that
after the abns/abnsz updates in 3.1, the connect() code was not adjusted
to take care of the change, resulting in AF_CUST_ABNS or AF_CUST_ABNSZ
to be placed in the address that was passed to connect().

The right approach is to locally copy the address, get its length,
fixup the family and use the fixed value and length for connect().

This must be backported to 3.1. Many thanks for @Mewp for reporting
this issue in github issue #2875.
2025-02-21 07:59:08 +01:00
Valentine Krasnobaeva
390df282c1 BUG/MINOR: cfgparse: fix NULL ptr dereference in cfg_parse_peers
When "peers" keyword is followed by more than one argument and it's the first
"peers" section in the config, cfg_parse_peers() detects it and exits with
"ERR_ALERT|ERR_FATAL" err_code.

So, upper layer parser, parse_cfg(), continues and parses the next keyword
"peer" and then he tries to check the global cfg_peers, which should contain
"my_cluster". The global cfg_peers is still NULL, because after alerting a user
in alertif_too_many_args, cfg_parse_peers() exited.

	peers my_cluster __some_wrong_data__
	peer haproxy1 1.1.1.1 1000

In order to fix this, let's add ERR_ABORT, if "peers" keyword is followed by
more than one argument. Like this parse_cfg() will stops immediately and
terminates haproxy with "too many args for peers my_cluster..." alert message.

It's more reliable, than add checks "if (cfg_peers !=NULL)" in "peer"
subparser, as we may have many "peers" sections.

	peers my_another_cluster
	peer haproxy1 1.1.1.2 1000

	peers my_cluster  __some_wrong_data__
	peer haproxy1 1.1.1.1 1000

In addition, for the example above, parse_cfg() will parse all configuration
until the end and only then terminates haproxy with the alert
"too many args...". Peer haproxy1 will be wrongly associated with
my_another_cluster.

This fixes the issue #2872.
This should be backported in all stable versions.
2025-02-20 17:10:26 +01:00
Christopher Faulet
851e52b551 BUG/MEDIUM: spoe/mux-spop: Introduce an NOOP action to deal with empty ACK
In the SPOP protocol, ACK frame with empty payload are allowed. However, in
that case, because only the payload is transferred, there is no data to
return to the SPOE applet. Only the end of input is reported. Thus the
applet is never woken up. It means that the SPOE filter will be blocked
during the processing timeout and will finally return an error.

To workaournd this issue, a NOOP action is introduced with the value 0. It
is only an internal action for now. It does not exist in the SPOP
protocol. When an ACK frame with an empy payload is received, this noop
action is transferred to the SPOE applet, instead of nothing. Thanks to this
trick, the applet is properly notified. This works because unknown actions
are ignored by the SPOE filter.

This patch must be backported to 3.1.
2025-02-20 11:56:27 +01:00
Christopher Faulet
efc46de294 BUG/MEDIUM: applet: Don't handle EOI/EOS/ERROR is applet is waiting for room
The commit 7214dcd52 ("BUG/MEDIUM: applet: Don't pretend to have more data
to handle EOI/EOS/ERROR") introduced a regression. Because of this patch, it
was possible to handle EOI/EOS/ERROR applet flags too early while the applet
was waiting for more room to transfer the last output data.

This bug can be encountered with any applet using its own buffers (cache and
stats for instance). And depending on the configuration and the timing, the
data may be truncated or the stream may be blocked, infinitely or not.
Streams blocked infinitely were observed with the cache applet and the HTTP
compression enabled.

For the record, it is important to detect EOI/EOS/ERROR applet flags to be
able to report the corresponding event on the SE and by transitivity on the
SC. Most of time, this happens when some data should be transferred to the
stream. The .rcv_buf callback function is called and these flags are
properly handled. However, some applets may also report them spontaneously,
outside of any data transfer. In that case, the .rcv_buf callback is not
called.

It is the purpose of this patch (and the one above). Being able to detect
pending EOI/EOS/ERROR applet flags. However, we must be sure to not handle
them too early at this place. When these flags are set, it means no more
data will be produced by the applet. So we must only wait to have
transferred everything to the stream. And this happens when the applet is no
longer waiting for more room.

This patch must be backported to 3.1 with the one above.
2025-02-20 10:00:32 +01:00
Willy Tarreau
4ef6be4a1f [RELEASE] Released version 3.2-dev6
Released version 3.2-dev6 with the following main changes :
    - BUG/MEDIUM: debug: close a possible race between thread dump and panic()
    - DEBUG: thread: report the spin lock counters as seek locks
    - DEBUG: thread: make lock time computation more consistent
    - DEBUG: thread: report the wait time buckets for lock classes
    - DEBUG: thread: don't keep the redundant _locked counter
    - DEBUG: thread: make lock_stat per operation instead of for all operations
    - DEBUG: thread: reduce the struct lock_stat to store only 30 buckets
    - MINOR: lbprm: add a new callback ->server_requeue to the lbprm
    - MEDIUM: server: allocate a tasklet for asyncronous requeuing
    - MAJOR: leastconn: postpone the server's repositioning under contention
    - BUG/MINOR: quic: reserve length field for long header encoding
    - BUG/MINOR: quic: fix CRYPTO payload size calcul for encoding
    - MINOR: quic: simplify length calculation for STREAM/CRYPTO frames
    - BUG/MINOR: mworker: section ignored in discovery after a post_section_parser
    - BUG/MINOR: mworker: post_section_parser for the last section in discovery
    - CLEANUP: mworker: "program" section does not have a post_section_parser anymore
    - MEDIUM: initcall: allow to register mutiple post_section_parser per section
    - CI: cirrus-ci: bump FreeBSD image to 14-2
    - DOC: initcall: name correctly REGISTER_CONFIG_POST_SECTION()
    - REGTESTS: stop using truncated.vtc on freebsd
    - MINOR: quic: refactor STREAM encoding and splitting
    - MINOR: quic: refactor CRYPTO encoding and splitting
    - BUG/MEDIUM: fd: mark FD transferred to another process as FD_CLONED
    - BUG/MINOR: ssl/cli: "show ssl crt-list" lacks client-sigals
    - BUG/MINOR: ssl/cli: "show ssl crt-list" lacks sigals
    - MINOR: ssl/cli: display more filenames in 'show ssl cert'
    - DOC: watchdog: document the sequence of the watchdog and panic
    - MINOR: ssl: store the filenames resulting from a lookup in ckch_conf
    - MINOR: startup: allow hap_register_feature() to enable a feature in the list
    - MINOR: quic: support frame type as a varint
    - BUG/MINOR: startup: leave at first post_section_parser which fails
    - BUG/MINOR: startup: hap_register_feature() fix for partial feature name
    - BUG/MEDIUM: cli: Be sure to drop all input data in END state
    - BUG/MINOR: cli: Wait for the last ACK when FDs are xferred from the old worker
    - BUG/MEDIUM: filters: Handle filters registered on data with no payload callback
    - BUG/MINOR: fcgi: Don't set the status to 302 if it is already set
    - MINOR: ssl/crtlist: split the ckch_conf loading from the crtlist line parsing
    - MINOR: ssl/crtlist: handle crt_path == cc->crt in crtlist_load_crt()
    - MINOR: ssl/ckch: return from ckch_conf_clean() when conf is NULL
    - MEDIUM: ssl/crtlist: "crt" keyword in frontend
    - DOC: configuration: document the "crt" frontend keyword
    - DEV: h2: add a Lua-based HTTP/2 connection tracer
    - BUG/MINOR: quic: prevent crash on conn access after MUX init failure
    - BUG/MINOR: mux-quic: prevent crash after MUX init failure
    - DEV: h2: fix flags for the continuation frame
    - REGTESTS: Fix truncated.vtc to send 0-CRLF
    - BUG/MINOR: mux-h2: Properly handle full or truncated HTX messages on shut
    - Revert "REGTESTS: stop using truncated.vtc on freebsd"
    - MINOR: mux-quic: define a QCC application state member
    - MINOR: mux-quic/h3: emit SETTINGS via MUX tasklet handler
    - MINOR: mux-quic/h3: support temporary blocking on control stream sending
2025-02-19 18:39:51 +01:00
Amaury Denoyelle
a7645d7cd5 MINOR: mux-quic/h3: support temporary blocking on control stream sending
When HTTP/3 layer is initialized via QUIC MUX, it first emits a SETTINGS
frame on an unidirectional control stream. However, this could be
prevented if client did not provide initial flow control.

Previously, QUIC MUX was unable to deal with such situation. Thus, the
connection was closed immediately and no transfer could occur. Improve
this by extending QUIC MUX application layer API : initialization may
now return a transient error. This allows MUX to continue to use the
connection normally. Initialization will be retried periodically alter
until it can succeed.

This new API allows to deal with the flow control issue described above.
Note that this patch is not considered as a bug fix. Indeed, clients are
strongly advised to provide enough flow control for a SETTINGS frame
exchange.
2025-02-19 11:08:02 +01:00
Amaury Denoyelle
06e7674399 MINOR: mux-quic/h3: emit SETTINGS via MUX tasklet handler
Previously, QUIC MUX application layer was installed and initialized via
MUX init. However, the latter stage involve I/O operations, for example
when using HTTP/3 with the emission of a SETTINGS frame.

Change this to prevent any I/O operations during MUX init. As such,
finalize app_ops callback is now called during the first invokation of
qcc_io_send(), in the context of MUX tasklet. To implement this, a new
application state value is added, to detect the transition from NULL to
INIT stage.
2025-02-19 11:03:40 +01:00
Amaury Denoyelle
188fc45b95 MINOR: mux-quic: define a QCC application state member
Introduce a new QCC field to track the current application layer state.
For the moment, only INIT and SHUT state are defined. This allows to
replace the older flag QC_CF_APP_SHUT.

This commit does not bring major changes. It is only necessary to permit
future evolutions on QUIC MUX. The only noticeable change is that QMUX
traces can now display this new field.
2025-02-19 10:59:53 +01:00
Christopher Faulet
4a99f15f0c Revert "REGTESTS: stop using truncated.vtc on freebsd"
This reverts commit 0b9a75e8781593c250f6366a64a019018ade688e.

Thanks to the previous fixes ("REGTESTS: Fix truncated.vtc to send 0-CRLF" and
"BUG/MINOR: mux-h2: Properly handle full or truncated HTX messages on shut"),
this script can be reenabled for FreeBSD.
2025-02-18 17:35:00 +01:00
Christopher Faulet
b70921f2c1 BUG/MINOR: mux-h2: Properly handle full or truncated HTX messages on shut
On shut, truncated HTX messages were not properly handled by the H2
multiplexer. Depending on how data were emitted, a chunked HTX message
without the 0-CRLF could be considered as full and an empty data with ES
flag set could be emitted instead of a RST_STREAM(CANCEL) frame.

In the H2 multiplexer, when a shut is performed, an HTX message is
considered as truncated if more HTX data are still expected. It is based on
the presence or not of the H2_SF_MORE_HTX_DATA flag on the H2 stream.
However, this flag is set or unset depending on the HTX extra field
value. This field is used to state how much data that must still be
transferred, based on the announced data length. For a message with a
content-length, this assumption is valid. But for a chunked message, it is
not true. Only the length of the current chunk is announced. So we cannot
rely on this field in that case to know if a message is full or not.

Instead, we must rely on the HTX start-line flags to know if more HTX data
are expected or not. If the xfer length is known (the HTX_SL_F_XFER_LEN flag
is set on the HTX start-line), it means that more data are always expected,
until the end of message is reached (the HTX_FL_EOM flag is set on the HTX
message). This is true for bodyless message because the end of message is
reported with the end of headers. This is also true for tunneled messages
because the end of message is received before switching the H2 stream in
tunnel mode.

This patch must be backported as far as 2.8.
2025-02-18 17:34:59 +01:00
Christopher Faulet
b93e419750 REGTESTS: Fix truncated.vtc to send 0-CRLF
When a chunked messages is sent, the 0-CRLF must be explicitely sent. Since
the begining, it is missing. Just add it.
2025-02-18 17:34:59 +01:00
Willy Tarreau
af5c07eee9 DEV: h2: fix flags for the continuation frame
It's flag 2 (end of headers) that's defined there, not 3 (padded).
2025-02-18 14:17:17 +01:00
Amaury Denoyelle
2715dbe9d0 BUG/MINOR: mux-quic: prevent crash after MUX init failure
qmux_init() may fail for several reasons. In this case, connection
resources are freed and underlying and a CONNECTION_CLOSE will be
emitted via its quic_conn instance.

In case of qmux_init() failure, qcc_release() is used to clean up
resources, but QCC <conn> member is first resetted to NULL, as
connection released must be delayed. Some cleanup operations are thus
skipped, one of them is the resetting of <ctx> connection member to
NULL. This may cause a crash as <ctx> is a dangling pointer after QCC
release. One of the possible reproducer is to activate QMUX traces,
which will cause a segfault on the qmux_init() error leave trace.

To fix this, simply reset <ctx> to NULL manually on qmux_init() failure.

This must be backported up to 3.0.
2025-02-18 11:02:46 +01:00
Amaury Denoyelle
2cdc4695cb BUG/MINOR: quic: prevent crash on conn access after MUX init failure
Initially, QUIC-MUX was responsible to reset quic_conn <conn> member to
NULL when MUX was released. This was performed via qcc_release().

However, qcc_release() is also used on qmux_init() failure. In this
case, connection must be freed via its session, so QCC <conn> member is
resetted to NULL prior to qcc_release(), which prevents quic_conn <conn>
member to also be resetted. As the connection is freed soon after,
quic_conn <conn> is a dangling pointer, which may cause crashes.

This bug should be very rare as first it implies that QUIC-MUX
initialization has failed (for example due to a memory alloc error).
Also, <conn> member is rarely used by quic_conn instance. In fact, the
only reproducible crash was done with QUIC traces activated, as in this
case connection is accessed via quic_conn under __trace_enabled()
function.

To fix this, detach connection from quic_conn via the XPRT layer instead
of the MUX. More precisely, this is performed via quic_close(). This
should ensure that it will always be conducted, either on normal
connection closure, but also after special conditions such as MUX init
failure.

This should be backported up to 2.6.
2025-02-18 10:43:56 +01:00
Willy Tarreau
607aa57b2e DEV: h2: add a Lua-based HTTP/2 connection tracer
The following config is sufficient to trace H2 exchanges between a client
and a server:

   global
       lua-load "dev/h2/h2-tracer.lua"

   listen h2_sniffer
       mode tcp
       bind :8002
       filter lua.h2-tracer #hex
       server s1 127.0.0.1:8003

The commented "hex" argument will also display full frames in hex (not
recommended). The connections are prefixed with a 3-hex digit number in
order to also support a bit of multiplexing without impacting the reading
too much. The screen is split in two, with the request on the left and
the response on the right. Here's an example of what it does between an
haproxy backend and an haproxy frontend both in H2, when submitted a
curl request for /?s=30k handled by httpterm:

  [001] ### req start
  [001] [PREFACE len=24]
  [001] [SETTINGS sid=0 len=24 (bytes=24)]
  [001]                                          | ### res start
  [001]                                          | [SETTINGS sid=0 len=18 (bytes=27)]
  [001]                                          | [SETTINGS ACK sid=0 len=0 (bytes=0)]
  [001] [SETTINGS ACK sid=0 len=0 (bytes=56)]
  [001] [HEADERS EH+ES sid=1 len=47 (bytes=47)]
  [001]                                          | [HEADERS EH sid=1 len=101 (bytes=15351)]
  [001]                                          | [DATA sid=1 len=15126 (bytes=15241)]
  [001]                                          | [DATA sid=1 len=1258 (bytes=106)]
  [001]                                          |                  ... -106 = 1152
  [001]                                          |                    ... -1152 = 0
  [001] [WINDOW_UPDATE sid=1 len=4 (bytes=43)]
  [001] [WINDOW_UPDATE sid=0 len=4 (bytes=30)]
  [001] [WINDOW_UPDATE sid=1 len=4 (bytes=17)]
  [001] [WINDOW_UPDATE sid=0 len=4 (bytes=4)]
  [001]                                          | [DATA ES sid=1 len=14336 (bytes=14336)]
  [001] [WINDOW_UPDATE sid=0 len=4 (bytes=4)]
  [001] ### req end: 31080 bytes total
  [001]                                          | [GOAWAY sid=0 len=8 (bytes=8)]
  [001]                                          | ### res end: 31097 bytes total

It deserves some improvements. For instance at the moment it does not
verify the preface, any 24 bytes will work. It does not perform any
protocol validation either. Detecting some issues such as out-of-sequence
frames could be helpful. But it already helps as-is.
2025-02-18 09:26:15 +01:00
William Lallemand
764f6910ed DOC: configuration: document the "crt" frontend keyword
Document the "crt" keyword of frontend and listen section.
2025-02-17 18:26:37 +01:00
William Lallemand
cd6a02ace9 MEDIUM: ssl/crtlist: "crt" keyword in frontend
This patch implements the "crt" keywords in frontend, declaring an
implicit crt-list named after the frontend.

The patch is split in two steps:

The first step is the crt keyword parser, which parses crt lines and
fill a "cfg_crt_node" struct containing a ssl_bind_conf and a
ckch_conf which are put in a list to be used later.

After parsing the frontend section, as a 2nd step, a
post_section_parser is called, it will create a crt-list named after
the frontend and will fill it with certificates from the list of
cfg_crt_node. Once created this crt-list will be loaded in every "ssl"
bind lines that didn't declare any crt or crt-list.

Example:

    listen https
       bind :443 ssl
       crt foobar.pem
       crt test1.net.crt key test1.net.key

Implements part of #2854
2025-02-17 18:26:37 +01:00
William Lallemand
82f927817e MINOR: ssl/ckch: return from ckch_conf_clean() when conf is NULL
ckch_conf_clean() mustn't be executed when the argument is NULL, this
will keep the API more consistant like any free() function.
2025-02-17 18:26:37 +01:00
William Lallemand
0330011acf MINOR: ssl/crtlist: handle crt_path == cc->crt in crtlist_load_crt()
Handle the case where crt_path == cc->crt, so the pointer doesn't get
free'd before getting strdup'ed in crtlist_load_crt().
2025-02-17 18:26:37 +01:00
William Lallemand
69163cd63e MINOR: ssl/crtlist: split the ckch_conf loading from the crtlist line parsing
ckch_conf loading is not that simple as it requires to check
- if the cert already exists in the ckchs_tree
- if the ckch_conf is compatible with an existing cert in ckchs_tree
- if the cert is a bundle which need to load multiple ckch_store

This logic could be reuse elsewhere, so this commit introduce the new
crtlist_load_crt() function which does that.
2025-02-17 18:26:37 +01:00
Christopher Faulet
ca79ed5eef BUG/MINOR: fcgi: Don't set the status to 302 if it is already set
When a "Location" header was found in a FCGI response, the status code was
forced to 302. But it should only be performed if no status code was set
first.

So now, we take care to not override an already defined status code when the
"Location" header is found.

This patch should fix the issue #2865. It must backported to all stable
versions.
2025-02-17 16:37:53 +01:00
Christopher Faulet
34542d5ec2 BUG/MEDIUM: filters: Handle filters registered on data with no payload callback
An HTTP filter with no http_payload callback function may be registered on
data. In that case, this filter is obviously not called when some data are
received but it remains important to update its internal state to be sure to
keep it synchronized on the stream, especially its offet value. Otherwise,
the wrong calculation on the global offset may be performed in
flt_http_end(), leading to an integer overflow when data are moved from
input to output. This overflow triggers a BUG_ON() in c_adv().

The same is true for TCP filters with no tcp_payload callback function.

This patch must be backport to all stable versions.
2025-02-17 16:16:29 +01:00
Christopher Faulet
49b7bcf583 BUG/MINOR: cli: Wait for the last ACK when FDs are xferred from the old worker
On reload, the new worker requests bound FDs to the old one. The old worker
sends them in message of at most 252 FDs. Each message is acknowledged by
the new worker. All messages sent or received by the old worker are handled
manually via sendmsg/recv syscalls. So the old worker must be sure consume
all the ACK replies. However, the last one was never consumed. So it was
considered as a command by the CLI applet. This issue was hidden since
recently. But it was the root cause of the issue #2862.

Note this last ack is also the first one when there are less than 252 FDs to
transfer.

This patch must be backported to all stable versions.
2025-02-17 15:31:07 +01:00
Christopher Faulet
972ce87676 BUG/MEDIUM: cli: Be sure to drop all input data in END state
Commit 7214dcd ("BUG/MEDIUM: applet: Don't pretend to have more data to
handle EOI/EOS/ERROR") revealed a bug with the CLI applet. Pending input
data when the applet is in CLI_ST_END state were never consumed or dropped,
leading to a wakeup loop.

The CLI applet implements its own snd_buf callback function. It is important
it consumes all pending input data. Otherwise, the applet is woken up in
loop until it empties the request buffer. Another way to fix the issue would
be to report an error. But in that case, it seems reasonnable to drop these
data.

The issue can be observed on reload, in master/worker mode, because of issue
about the last ACK message which was never consummed by the _getsocks()
command.

This patch should fix the issue #2862. It must be backported to 3.1 with the
commit above.
2025-02-17 15:31:07 +01:00
342 changed files with 25453 additions and 7592 deletions

View File

@ -20,8 +20,8 @@ defaults
frontend h2
mode http
bind 127.0.0.1:8443 ssl crt reg-tests/ssl/common.pem alpn h2,http/1.1
default_backend h2
default_backend h2b
backend h2
backend h2b
errorfile 200 .github/errorfile
http-request deny deny_status 200

View File

@ -5,82 +5,8 @@ on:
- cron: "0 0 * * 4"
workflow_dispatch:
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install VTest
run: |
scripts/build-vtest.sh
- name: Determine latest AWS-LC release
id: get_aws_lc_release
run: |
result=$(cd .github && python3 -c "from matrix import determine_latest_aws_lc_fips; print(determine_latest_aws_lc_fips(''))")
echo $result
echo "result=$result" >> $GITHUB_OUTPUT
- name: Cache AWS-LC
id: cache_aws_lc
uses: actions/cache@v4
with:
path: '~/opt/'
key: ssl-${{ steps.get_aws_lc_release.outputs.result }}-Ubuntu-latest-gcc
- name: Install apt dependencies
run: |
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
sudo apt-get --no-install-recommends -y install socat gdb
- name: Install AWS-LC
if: ${{ steps.cache_ssl.outputs.cache-hit != 'true' }}
run: env ${{ steps.get_aws_lc_release.outputs.result }} scripts/build-ssl.sh
- name: Compile HAProxy
run: |
make -j$(nproc) ERR=1 CC=gcc TARGET=linux-glibc \
USE_OPENSSL_AWSLC=1 USE_QUIC=1 \
SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include \
DEBUG="-DDEBUG_POOL_INTEGRITY" \
ADDLIB="-Wl,-rpath,/usr/local/lib/ -Wl,-rpath,$HOME/opt/lib/"
sudo make install
- name: Show HAProxy version
id: show-version
run: |
ldd $(which haproxy)
haproxy -vv
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
- name: Install problem matcher for VTest
run: echo "::add-matcher::.github/vtest.json"
- name: Run VTest for HAProxy
id: vtest
run: |
# This is required for macOS which does not actually allow to increase
# the '-n' soft limit to the hard limit, thus failing to run.
ulimit -n 65536
# allow to catch coredumps
ulimit -c unlimited
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
- name: Show VTest results
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
for folder in ${TMPDIR:-/tmp}/haregtests-*/vtc.*; do
printf "::group::"
cat $folder/INFO
cat $folder/LOG
echo "::endgroup::"
done
exit 1
- name: Show coredumps
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
failed=false
shopt -s nullglob
for file in /tmp/core.*; do
failed=true
printf "::group::"
gdb -ex 'thread apply all bt full' ./haproxy $file
echo "::endgroup::"
done
if [ "$failed" = true ]; then
exit 1;
fi
uses: ./.github/workflows/aws-lc-template.yml
with:
command: "from matrix import determine_latest_aws_lc_fips; print(determine_latest_aws_lc_fips(''))"

103
.github/workflows/aws-lc-template.yml vendored Normal file
View File

@ -0,0 +1,103 @@
name: AWS-LC template
on:
workflow_call:
inputs:
command:
required: true
type: string
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v4
- name: Install VTest
run: |
scripts/build-vtest.sh
- name: Determine latest AWS-LC release
id: get_aws_lc_release
run: |
result=$(cd .github && python3 -c "${{ inputs.command }}")
echo $result
echo "result=$result" >> $GITHUB_OUTPUT
- name: Cache AWS-LC
id: cache_aws_lc
uses: actions/cache@v4
with:
path: '~/opt/'
key: ssl-${{ steps.get_aws_lc_release.outputs.result }}-Ubuntu-latest-gcc
- name: Install apt dependencies
run: |
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
sudo apt-get --no-install-recommends -y install socat gdb jose
- name: Install AWS-LC
if: ${{ steps.cache_ssl.outputs.cache-hit != 'true' }}
run: env ${{ steps.get_aws_lc_release.outputs.result }} scripts/build-ssl.sh
- name: Compile HAProxy
run: |
make -j$(nproc) ERR=1 CC=gcc TARGET=linux-glibc \
USE_OPENSSL_AWSLC=1 USE_QUIC=1 \
SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include \
DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" \
ADDLIB="-Wl,-rpath,/usr/local/lib/ -Wl,-rpath,$HOME/opt/lib/"
sudo make install
- name: Show HAProxy version
id: show-version
run: |
ldd $(which haproxy)
haproxy -vv
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
- name: Install problem matcher for VTest
run: echo "::add-matcher::.github/vtest.json"
- name: Run VTest for HAProxy
id: vtest
run: |
# This is required for macOS which does not actually allow to increase
# the '-n' soft limit to the hard limit, thus failing to run.
ulimit -n 65536
# allow to catch coredumps
ulimit -c unlimited
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
- name: Run Unit tests
id: unittests
run: |
make unit-tests
- name: Show VTest results
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
for folder in ${TMPDIR:-/tmp}/haregtests-*/vtc.*; do
printf "::group::"
cat $folder/INFO
cat $folder/LOG
echo "::endgroup::"
done
exit 1
- name: Show coredumps
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
failed=false
shopt -s nullglob
for file in /tmp/core.*; do
failed=true
printf "::group::"
gdb -ex 'thread apply all bt full' ./haproxy $file
echo "::endgroup::"
done
if [ "$failed" = true ]; then
exit 1;
fi
- name: Show Unit-Tests results
if: ${{ failure() && steps.unittests.outcome == 'failure' }}
run: |
for result in ${TMPDIR:-/tmp}/ha-unittests-*/results/res.*; do
printf "::group::"
cat $result
echo "::endgroup::"
done
exit 1

View File

@ -5,82 +5,8 @@ on:
- cron: "0 0 * * 4"
workflow_dispatch:
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install VTest
run: |
scripts/build-vtest.sh
- name: Determine latest AWS-LC release
id: get_aws_lc_release
run: |
result=$(cd .github && python3 -c "from matrix import determine_latest_aws_lc; print(determine_latest_aws_lc(''))")
echo $result
echo "result=$result" >> $GITHUB_OUTPUT
- name: Cache AWS-LC
id: cache_aws_lc
uses: actions/cache@v4
with:
path: '~/opt/'
key: ssl-${{ steps.get_aws_lc_release.outputs.result }}-Ubuntu-latest-gcc
- name: Install apt dependencies
run: |
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
sudo apt-get --no-install-recommends -y install socat gdb
- name: Install AWS-LC
if: ${{ steps.cache_ssl.outputs.cache-hit != 'true' }}
run: env ${{ steps.get_aws_lc_release.outputs.result }} scripts/build-ssl.sh
- name: Compile HAProxy
run: |
make -j$(nproc) ERR=1 CC=gcc TARGET=linux-glibc \
USE_OPENSSL_AWSLC=1 USE_QUIC=1 \
SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include \
DEBUG="-DDEBUG_POOL_INTEGRITY" \
ADDLIB="-Wl,-rpath,/usr/local/lib/ -Wl,-rpath,$HOME/opt/lib/"
sudo make install
- name: Show HAProxy version
id: show-version
run: |
ldd $(which haproxy)
haproxy -vv
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
- name: Install problem matcher for VTest
run: echo "::add-matcher::.github/vtest.json"
- name: Run VTest for HAProxy
id: vtest
run: |
# This is required for macOS which does not actually allow to increase
# the '-n' soft limit to the hard limit, thus failing to run.
ulimit -n 65536
# allow to catch coredumps
ulimit -c unlimited
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
- name: Show VTest results
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
for folder in ${TMPDIR:-/tmp}/haregtests-*/vtc.*; do
printf "::group::"
cat $folder/INFO
cat $folder/LOG
echo "::endgroup::"
done
exit 1
- name: Show coredumps
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
failed=false
shopt -s nullglob
for file in /tmp/core.*; do
failed=true
printf "::group::"
gdb -ex 'thread apply all bt full' ./haproxy $file
echo "::endgroup::"
done
if [ "$failed" = true ]; then
exit 1;
fi
uses: ./.github/workflows/aws-lc-template.yml
with:
command: "from matrix import determine_latest_aws_lc; print(determine_latest_aws_lc(''))"

View File

@ -3,6 +3,7 @@ name: Spelling Check
on:
schedule:
- cron: "0 0 * * 2"
workflow_dispatch:
permissions:
contents: read
@ -10,12 +11,12 @@ permissions:
jobs:
codespell:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' }}
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v4
- uses: codespell-project/codespell-problem-matcher@v1.2.0
- uses: codespell-project/actions-codespell@master
with:
skip: CHANGELOG,Makefile,*.fig,*.pem,./doc/design-thoughts,./doc/internals
ignore_words_list: ist,ists,hist,wan,ca,cas,que,ans,te,nd,referer,ot,uint,iif,fo,keep-alives,dosen,ifset,thrid,strack,ba,chck,hel,unx,mor,clen,collet,bu,htmp,siz,experim
ignore_words_list: pres,ist,ists,hist,wan,ca,cas,que,ans,te,nd,referer,ot,uint,iif,fo,keep-alives,dosen,ifset,thrid,strack,ba,chck,hel,unx,mor,clen,collet,bu,htmp,siz,experim
uri_ignore_words_list: trafic,ressources

View File

@ -11,13 +11,8 @@ permissions:
jobs:
h2spec:
name: h2spec
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- TARGET: linux-glibc
CC: gcc
os: ubuntu-latest
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v4
- name: Install h2spec
@ -28,12 +23,12 @@ jobs:
tar xvf h2spec.tar.gz
sudo install -m755 h2spec /usr/local/bin/h2spec
echo "version=${H2SPEC_VERSION}" >> $GITHUB_OUTPUT
- name: Compile HAProxy with ${{ matrix.CC }}
- name: Compile HAProxy with gcc
run: |
make -j$(nproc) all \
ERR=1 \
TARGET=${{ matrix.TARGET }} \
CC=${{ matrix.CC }} \
TARGET=linux-glibc \
CC=gcc \
DEBUG="-DDEBUG_POOL_INTEGRITY" \
USE_OPENSSL=1
sudo make install

View File

@ -15,7 +15,7 @@ permissions:
jobs:
scan:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' }}
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v4
- name: Install apt dependencies

View File

@ -91,7 +91,7 @@ jobs:
}
]
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' }}
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- name: install packages
run: |

View File

@ -20,14 +20,14 @@ jobs:
]
name: ${{ matrix.platform.cc }}.${{ matrix.platform.name }}
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' }}
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
container:
image: fedora:rawhide
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
dnf -y install diffutils git pcre-devel zlib-devel pcre2-devel 'perl(FindBin)' perl-IPC-Cmd 'perl(File::Copy)' 'perl(File::Compare)' lua-devel socat findutils systemd-devel clang
dnf -y install awk diffutils git pcre-devel zlib-devel pcre2-devel 'perl(FindBin)' perl-IPC-Cmd 'perl(File::Copy)' 'perl(File::Compare)' lua-devel socat findutils systemd-devel clang
dnf -y install 'perl(FindBin)' 'perl(File::Compare)' perl-IPC-Cmd 'perl(File::Copy)' glibc-devel.i686 lua-devel.i686 lua-devel.x86_64 systemd-devel.i686 zlib-ng-compat-devel.i686 pcre-devel.i686 libatomic.i686
- name: Install VTest
run: scripts/build-vtest.sh
@ -41,7 +41,7 @@ jobs:
make dev/hpack/decode dev/hpack/gen-enc dev/hpack/gen-rht
- name: Compile HAProxy with ${{ matrix.platform.cc }}
run: |
make -j3 CC=${{ matrix.platform.cc }} V=1 ERR=1 TARGET=linux-glibc USE_OPENSSL=1 USE_QUIC=1 USE_ZLIB=1 USE_PCRE=1 USE_PCRE_JIT=1 USE_LUA=1 ADDLIB="${{ matrix.platform.ADDLIB_ATOMIC }} -Wl,-rpath,${HOME}/opt/lib" SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include ARCH_FLAGS="${{ matrix.platform.ARCH_FLAGS }}"
make -j3 CC=${{ matrix.platform.cc }} V=1 ERR=1 TARGET=linux-glibc DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" USE_OPENSSL=1 USE_QUIC=1 USE_ZLIB=1 USE_PCRE=1 USE_PCRE_JIT=1 USE_LUA=1 ADDLIB="${{ matrix.platform.ADDLIB_ATOMIC }} -Wl,-rpath,${HOME}/opt/lib" SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include ARCH_FLAGS="${{ matrix.platform.ARCH_FLAGS }}"
make install
- name: Show HAProxy version
id: show-version
@ -64,3 +64,7 @@ jobs:
cat $folder/LOG
echo "::endgroup::"
done
- name: Run Unit tests
id: unittests
run: |
make unit-tests

View File

@ -8,7 +8,7 @@ on:
jobs:
gcc:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' }}
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
permissions:
contents: read
steps:

View File

@ -22,11 +22,11 @@ jobs:
echo '/tmp/core/core.%h.%e.%t' > /proc/sys/kernel/core_pattern
- uses: actions/checkout@v4
- name: Install dependencies
run: apk add gcc gdb make tar git python3 libc-dev linux-headers pcre-dev pcre2-dev openssl-dev lua5.3-dev grep socat curl musl-dbg lua5.3-dbg
run: apk add gcc gdb make tar git python3 libc-dev linux-headers pcre-dev pcre2-dev openssl-dev lua5.3-dev grep socat curl musl-dbg lua5.3-dbg jose
- name: Install VTest
run: scripts/build-vtest.sh
- name: Build
run: make -j$(nproc) TARGET=linux-musl ARCH_FLAGS='-ggdb3' CC=cc V=1 USE_LUA=1 LUA_INC=/usr/include/lua5.3 LUA_LIB=/usr/lib/lua5.3 USE_OPENSSL=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_PROMEX=1
run: make -j$(nproc) TARGET=linux-musl DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" ARCH_FLAGS='-ggdb3' CC=cc V=1 USE_LUA=1 LUA_INC=/usr/include/lua5.3 LUA_LIB=/usr/lib/lua5.3 USE_OPENSSL=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_PROMEX=1
- name: Show version
run: ./haproxy -vv
- name: Show linked libraries
@ -37,6 +37,10 @@ jobs:
- name: Run VTest
id: vtest
run: make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
- name: Run Unit tests
id: unittests
run: |
make unit-tests
- name: Show coredumps
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
@ -60,3 +64,13 @@ jobs:
cat $folder/LOG
echo "::endgroup::"
done
- name: Show Unit-Tests results
if: ${{ failure() && steps.unittests.outcome == 'failure' }}
run: |
for result in ${TMPDIR:-/tmp}/ha-unittests-*/results/res.*; do
printf "::group::"
cat $result
echo "::endgroup::"
done
exit 1

View File

@ -8,7 +8,7 @@ on:
jobs:
gcc:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' }}
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
permissions:
contents: read
steps:

View File

@ -13,7 +13,7 @@ on:
jobs:
build:
runs-on: ubuntu-24.04
if: ${{ github.repository_owner == 'haproxy' }}
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
permissions:
contents: read
packages: write
@ -38,6 +38,15 @@ jobs:
SSLLIB: AWS-LC
tags: ghcr.io/${{ github.repository }}:aws-lc
- name: Cleanup registry
uses: actions/delete-package-versions@v5
with:
owner: ${{ github.repository_owner }}
package-name: 'haproxy'
package-type: container
min-versions-to-keep: 1
delete-only-untagged-versions: 'true'
run:
needs: build
strategy:
@ -52,7 +61,7 @@ jobs:
name: ${{ matrix.suite.client }}
runs-on: ubuntu-24.04
if: ${{ github.repository_owner == 'haproxy' }}
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v4

View File

@ -13,7 +13,7 @@ on:
jobs:
build:
runs-on: ubuntu-24.04
if: ${{ github.repository_owner == 'haproxy' }}
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
permissions:
contents: read
packages: write
@ -38,6 +38,15 @@ jobs:
SSLLIB: LibreSSL
tags: ghcr.io/${{ github.repository }}:libressl
- name: Cleanup registry
uses: actions/delete-package-versions@v5
with:
owner: ${{ github.repository_owner }}
package-name: 'haproxy'
package-type: container
min-versions-to-keep: 1
delete-only-untagged-versions: 'true'
run:
needs: build
strategy:
@ -50,7 +59,7 @@ jobs:
name: ${{ matrix.suite.client }}
runs-on: ubuntu-24.04
if: ${{ github.repository_owner == 'haproxy' }}
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v4

83
.github/workflows/quictls.yml vendored Normal file
View File

@ -0,0 +1,83 @@
#
# weekly run against modern QuicTLS branch, i.e. https://github.com/quictls/quictls
#
name: QuicTLS
on:
schedule:
- cron: "0 0 * * 4"
workflow_dispatch:
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v4
- name: Install VTest
run: |
scripts/build-vtest.sh
- name: Install apt dependencies
run: |
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
sudo apt-get --no-install-recommends -y install socat gdb
- name: Install QuicTLS
run: env QUICTLS=yes QUICTLS_URL=https://github.com/quictls/quictls scripts/build-ssl.sh
- name: Compile HAProxy
run: |
make -j$(nproc) ERR=1 CC=gcc TARGET=linux-glibc \
USE_QUIC=1 USE_OPENSSL=1 \
SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include \
DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" \
ADDLIB="-Wl,-rpath,/usr/local/lib/ -Wl,-rpath,$HOME/opt/lib/" \
ARCH_FLAGS="-ggdb3 -fsanitize=address"
sudo make install
- name: Show HAProxy version
id: show-version
run: |
ldd $(which haproxy)
haproxy -vv
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
- name: Install problem matcher for VTest
run: echo "::add-matcher::.github/vtest.json"
- name: Run VTest for HAProxy
id: vtest
run: |
# This is required for macOS which does not actually allow to increase
# the '-n' soft limit to the hard limit, thus failing to run.
ulimit -n 65536
# allow to catch coredumps
ulimit -c unlimited
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
- name: Show VTest results
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
for folder in ${TMPDIR:-/tmp}/haregtests-*/vtc.*; do
printf "::group::"
cat $folder/INFO
cat $folder/LOG
echo "::endgroup::"
done
exit 1
- name: Run Unit tests
id: unittests
run: |
make unit-tests
- name: Show coredumps
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
failed=false
shopt -s nullglob
for file in /tmp/core.*; do
failed=true
printf "::group::"
gdb -ex 'thread apply all bt full' ./haproxy $file
echo "::endgroup::"
done
if [ "$failed" = true ]; then
exit 1;
fi

View File

@ -86,7 +86,8 @@ jobs:
${{ contains(matrix.FLAGS, 'USE_PCRE2=1') && 'libpcre2-dev' || '' }} \
${{ contains(matrix.ssl, 'BORINGSSL=yes') && 'ninja-build' || '' }} \
socat \
gdb
gdb \
jose
- name: Install brew dependencies
if: ${{ startsWith(matrix.os, 'macos-') }}
run: |
@ -117,7 +118,7 @@ jobs:
ERR=1 \
TARGET=${{ matrix.TARGET }} \
CC=${{ matrix.CC }} \
DEBUG="-DDEBUG_POOL_INTEGRITY" \
DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" \
${{ join(matrix.FLAGS, ' ') }} \
ADDLIB="-Wl,-rpath,/usr/local/lib/ -Wl,-rpath,$HOME/opt/lib/"
sudo make install
@ -146,14 +147,6 @@ jobs:
ulimit -n 65536
ulimit -c unlimited
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
- name: Config syntax check memleak smoke testing
if: ${{ contains(matrix.name, 'ASAN') }}
run: |
./haproxy -dI -f .github/h2spec.config -c
./haproxy -dI -f examples/content-sw-sample.cfg -c
./haproxy -dI -f examples/option-http_proxy.cfg -c
./haproxy -dI -f examples/quick-test.cfg -c
./haproxy -dI -f examples/transparent_proxy.cfg -c
- name: Show VTest results
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
@ -164,7 +157,19 @@ jobs:
echo "::endgroup::"
done
exit 1
- name: Run Unit tests
id: unittests
run: |
make unit-tests
- name: Show Unit-Tests results
if: ${{ failure() && steps.unittests.outcome == 'failure' }}
run: |
for result in ${TMPDIR:-/tmp}/ha-unittests-*/results/res.*; do
printf "::group::"
cat $result
echo "::endgroup::"
done
exit 1
- name: Show coredumps
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |

View File

@ -11,6 +11,7 @@ permissions:
jobs:
test:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v4
- name: Install VTest
@ -19,7 +20,7 @@ jobs:
- name: Install apt dependencies
run: |
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
sudo apt-get --no-install-recommends -y install socat gdb
sudo apt-get --no-install-recommends -y install socat gdb jose
- name: Install WolfSSL
run: env WOLFSSL_VERSION=git-master WOLFSSL_DEBUG=1 scripts/build-ssl.sh
- name: Compile HAProxy
@ -27,7 +28,7 @@ jobs:
make -j$(nproc) ERR=1 CC=gcc TARGET=linux-glibc \
USE_OPENSSL_WOLFSSL=1 USE_QUIC=1 \
SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include \
DEBUG="-DDEBUG_POOL_INTEGRITY" \
DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" \
ADDLIB="-Wl,-rpath,/usr/local/lib/ -Wl,-rpath,$HOME/opt/lib/" \
ARCH_FLAGS="-ggdb3 -fsanitize=address"
sudo make install
@ -48,6 +49,10 @@ jobs:
# allow to catch coredumps
ulimit -c unlimited
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
- name: Run Unit tests
id: unittests
run: |
make unit-tests
- name: Show VTest results
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
@ -72,3 +77,13 @@ jobs:
if [ "$failed" = true ]; then
exit 1;
fi
- name: Show Unit-Tests results
if: ${{ failure() && steps.unittests.outcome == 'failure' }}
run: |
for result in ${TMPDIR:-/tmp}/ha-unittests-*/results/res.*; do
printf "::group::"
cat $result
echo "::endgroup::"
done
exit 1

859
CHANGELOG
View File

@ -1,6 +1,865 @@
ChangeLog :
===========
2025/06/11 : 3.3-dev1
- BUILD: tools: properly define ha_dump_backtrace() to avoid a build warning
- DOC: config: Fix a typo in 2.7 (Name format for maps and ACLs)
- REGTESTS: Do not use REQUIRE_VERSION for HAProxy 2.5+ (5)
- REGTESTS: Remove REQUIRE_VERSION=2.3 from all tests
- REGTESTS: Remove REQUIRE_VERSION=2.4 from all tests
- REGTESTS: Remove tests with REQUIRE_VERSION_BELOW=2.4
- REGTESTS: Remove support for REQUIRE_VERSION and REQUIRE_VERSION_BELOW
- MINOR: server: group postinit server tasks under _srv_postparse()
- MINOR: stats: add stat_col flags
- MINOR: stats: add ME_NEW_COMMON() helper
- MINOR: proxy: collect per-capability stat in proxy_cond_disable()
- MINOR: proxy: add a true list containing all proxies
- MINOR: log: only run postcheck_log_backend() checks on backend
- MEDIUM: proxy: use global proxy list for REGISTER_POST_PROXY_CHECK() hook
- MEDIUM: server: automatically add server to proxy list in new_server()
- MEDIUM: server: add and use srv_init() function
- BUG/MAJOR: leastconn: Protect tree_elt with the lbprm lock
- BUG/MEDIUM: check: Requeue healthchecks on I/O events to handle check timeout
- CLEANUP: applet: Update comment for applet_put* functions
- DEBUG: check: Add the healthcheck's expiration date in the trace messags
- BUG/MINOR: mux-spop: Fix null-pointer deref on SPOP stream allocation failure
- CLEANUP: sink: remove useless cleanup in sink_new_from_logger()
- MAJOR: counters: add shared counters base infrastructure
- MINOR: counters: add shared counters helpers to get and drop shared pointers
- MINOR: counters: add common struct and flags to {fe,be}_counters_shared
- MEDIUM: counters: manage shared counters using dedicated helpers
- CLEANUP: counters: merge some common counters between {fe,be}_counters_shared
- MINOR: counters: add local-only internal rates to compute some maxes
- MAJOR: counters: dispatch counters over thread groups
- BUG/MEDIUM: cli: Properly parse empty lines and avoid crashed
- BUG/MINOR: config: emit warning for empty args only in discovery mode
- BUG/MINOR: config: fix arg number reported on empty arg warning
- BUG/MINOR: quic: Missing SSL session object freeing
- MINOR: applet: Add API functions to manipulate input and output buffers
- MINOR: applet: Add API functions to get data from the input buffer
- CLEANUP: applet: Simplify a bit comments for applet_put* functions
- MEDIUM: hlua: Update TCP applet functions to use the new applet API
- BUG/MEDIUM: fd: Use the provided tgid in fd_insert() to get tgroup_info
- BUG/MINIR: h1: Fix doc of 'accept-unsafe-...-request' about URI parsing
2025/05/28 : 3.3-dev0
- MINOR: version: mention that it's development again
2025/05/28 : 3.2.0
- MINOR: promex: Add agent check status/code/duration metrics
- MINOR: ssl: support strict-sni in ssl-default-bind-options
- MINOR: ssl: also provide the "tls-tickets" bind option
- MINOR: server: define CLI I/O handler for "add server"
- MINOR: server: implement "add server help"
- MINOR: server: use stress mode for "add server help"
- BUG/MEDIUM: server: fix crash after duplicate GUID insertion
- BUG/MEDIUM: server: fix potential null-deref after previous fix
- MINOR: config: list recently added sections with -dKcfg
- BUG/MAJOR: cache: Crash because of wrong cache entry deleted
- DOC: configuration: fix the example in crt-store
- DOC: config: clarify the wording around single/double quotes
- DOC: config: clarify the legacy cookie and header captures
- DOC: config: fix alphabetical ordering of layer 7 sample fetch functions
- DOC: config: fix alphabetical ordering of layer 6 sample fetch functions
- DOC: config: fix alphabetical ordering of layer 5 sample fetch functions
- DOC: config: fix alphabetical ordering of layer 4 sample fetch functions
- DOC: config: fix alphabetical ordering of internal sample fetch functions
- BUG/MINOR: h3: Set HTX flags corresponding to the scheme found in the request
- BUG/MEDIUM: h3: Declare absolute URI as normalized when a :authority is found
- DOC: config: mention in bytes_in and bytes_out that they're read on input
- DOC: config: clarify the basics of ACLs (call point, multi-valued etc)
- REGTESTS: Make the script testing conditional set-var compatible with Vtest2
- REGTESTS: Explicitly allow failing shell commands in some scripts
- MINOR: listeners: Add support for a label on bind line
- BUG/MEDIUM: cli/ring: Properly handle shutdown in "show event" I/O handler
- BUG/MEDIUM: hlua: Properly detect shudowns for TCP applets based on the new API
- BUG/MEDIUM: hlua: Fix getline() for TCP applets to work with applet's buffers
- BUG/MEDIUM: hlua: Fix receive API for TCP applets to properly handle shutdowns
- CI: vtest: Rely on VTest2 to run regression tests
- CI: vtest: Fix the build script to properly work on MaOS
- CI: combine AWS-LC and AWS-LC-FIPS by template
- BUG/MEDIUM: httpclient: Throw an error if an lua httpclient instance is reused
- DOC: hlua: Add a note to warn user about httpclient object reuse
- DOC: hlua: fix a few typos in HTTPMessage.set_body_len() documentation
- DEV: patchbot: prepare for new version 3.3-dev
- MINOR: version: mention that it's 3.2 LTS now.
2025/05/21 : 3.2-dev17
- DOC: configuration: explicit multi-choice on bind shards option
- BUG/MINOR: sink: detect and warn when using "send-proxy" options with ring servers
- BUG/MEDIUM: peers: also limit the number of incoming updates
- MEDIUM: hlua: Add function to change the body length of an HTTP Message
- BUG/MEDIUM: stconn: Disable 0-copy forwarding for filters altering the payload
- BUG/MINOR: h3: don't insert more than one Host header
- BUG/MEDIUM: h1/h2/h3: reject forbidden chars in the Host header field
- DOC: config: properly index "table and "stick-table" in their section
- DOC: management: change reference to configuration manual
- BUILD: debug: mark ha_crash_now() as attribute(noreturn)
- IMPORT: slz: avoid multiple shifts on 64-bits
- IMPORT: slz: support crc32c for lookup hash on sse4 but only if requested
- IMPORT: slz: use a better hash for machines with a fast multiply
- IMPORT: slz: fix header used for empty zlib message
- IMPORT: slz: silence a build warning on non-x86 non-arm
- BUG/MAJOR: leastconn: do not loop forever when facing saturated servers
- BUG/MAJOR: queue: properly keep count of the queue length
- BUG/MINOR: quic: fix crash on quic_conn alloc failure
- BUG/MAJOR: leastconn: never reuse the node after dropping the lock
- MINOR: acme: renewal notification over the dpapi sink
- CLEANUP: quic: Useless BIO_METHOD initialization
- MINOR: quic: Add useful error traces about qc_ssl_sess_init() failures
- MINOR: quic: Allow the use of the new OpenSSL 3.5.0 QUIC TLS API (to be completed)
- MINOR: quic: implement all remaining callbacks for OpenSSL 3.5 QUIC API
- MINOR: quic: OpenSSL 3.5 internal QUIC custom extension for transport parameters reset
- MINOR: quic: OpenSSL 3.5 trick to support 0-RTT
- DOC: update INSTALL for QUIC with OpenSSL 3.5 usages
- DOC: management: update 'acme status'
- BUG/MEDIUM: wdt: always ignore the first watchdog wakeup
- CLEANUP: wdt: clarify the comments on the common exit path
- BUILD: ssl: avoid possible printf format warning in traces
- BUILD: acme: fix build issue on 32-bit archs with 64-bit time_t
- DOC: management: precise some of the fields of "show servers conn"
- BUG/MEDIUM: mux-quic: fix BUG_ON() on rxbuf alloc error
- DOC: watchdog: update the doc to reflect the recent changes
- BUG/MEDIUM: acme: check if acme domains are configured
- BUG/MINOR: acme: fix formatting issue in error and logs
- EXAMPLES: lua: avoid screen refresh effect in "trisdemo"
- CLEANUP: quic: remove unused cbuf module
- MINOR: quic: move function to check stream type in utils
- MINOR: quic: refactor handling of streams after MUX release
- MINOR: quic: add some missing includes
- MINOR: quic: adjust quic_conn-t.h include list
- CLEANUP: cfgparse: alphabetically sort the global keywords
- MINOR: glitches: add global setting "tune.glitches.kill.cpu-usage"
2025/05/14 : 3.2-dev16
- BUG/MEDIUM: mux-quic: fix crash on invalid fctl frame dereference
- DEBUG: pool: permit per-pool UAF configuration
- MINOR: acme: add the global option 'acme.scheduler'
- DEBUG: pools: add a new integrity mode "backup" to copy the released area
- MEDIUM: sock-inet: re-check IPv6 connectivity every 30s
- BUG/MINOR: ssl: doesn't fill conf->crt with first arg
- BUG/MINOR: ssl: prevent multiple 'crt' on the same ssl-f-use line
- BUG/MINOR: ssl/ckch: always free() the previous entry during parsing
- MINOR: tools: ha_freearray() frees an array of string
- BUG/MINOR: ssl/ckch: always ha_freearray() the previous entry during parsing
- MINOR: ssl/ckch: warn when the same keyword was used twice
- BUG/MINOR: threads: fix soft-stop without multithreading support
- BUG/MINOR: tools: improve parse_line()'s robustness against empty args
- BUG/MINOR: cfgparse: improve the empty arg position report's robustness
- BUG/MINOR: server: dont depend on proxy for server cleanup in srv_drop()
- BUG/MINOR: server: perform lbprm deinit for dynamic servers
- MINOR: http: add a function to validate characters of :authority
- BUG/MEDIUM: h2/h3: reject some forbidden chars in :authority before reassembly
- MINOR: quic: account Tx data per stream
- MINOR: mux-quic: account Rx data per stream
- MINOR: quic: add stream format for "show quic"
- MINOR: quic: display QCS info on "show quic stream"
- MINOR: quic: display stream age
- BUG/MINOR: cpu-topo: fix group-by-cluster policy for disordered clusters
- MINOR: cpu-topo: add a new "group-by-ccx" CPU policy
- MINOR: cpu-topo: provide a function to sort clusters by average capacity
- MEDIUM: cpu-topo: change "performance" to consider per-core capacity
- MEDIUM: cpu-topo: change "efficiency" to consider per-core capacity
- MEDIUM: cpu-topo: prefer grouping by CCX for "performance" and "efficiency"
- MEDIUM: config: change default limits to 1024 threads and 32 groups
- BUG/MINOR: hlua: Fix Channel:data() and Channel:line() to respect documentation
- DOC: config: Fix a typo in the "term_events" definition
- BUG/MINOR: spoe: Don't report error on applet release if filter is in DONE state
- BUG/MINOR: mux-spop: Don't report error for stream if ACK was already received
- BUG/MINOR: mux-spop: Make the demux stream ID a signed integer
- BUG/MINOR: mux-spop: Don't open new streams for SPOP connection on error
- MINOR: mux-spop: Don't set SPOP connection state to FRAME_H after ACK parsing
- BUG/MEDIUM: mux-spop: Remove frame parsing states from the SPOP connection state
- BUG/MEDIUM: mux-spop: Properly handle CLOSING state
- BUG/MEDIUM: spop-conn: Report short read for partial frames payload
- BUG/MEDIUM: mux-spop: Properly detect truncated frames on demux to report error
- BUG/MEDIUM: mux-spop; Don't report a read error if there are pending data
- DEBUG: mux-spop: Review some trace messages to adjust the message or the level
- DOC: config: move address formats definition to section 2
- DOC: config: move stick-tables and peers to their own section
- DOC: config: move the extraneous sections out of the "global" definition
- CI: AWS-LC(fips): enable unit tests
- CI: AWS-LC: enable unit tests
- CI: compliance: limit run on forks only to manual + cleanup
- CI: musl: enable unit tests
- CI: QuicTLS (weekly): limit run on forks only to manual dispatch
- CI: WolfSSL: enable unit tests
2025/05/09 : 3.2-dev15
- BUG/MEDIUM: stktable: fix sc_*(<ctr>) BUG_ON() regression with ctx > 9
- BUG/MINOR: acme/cli: don't output error on success
- BUG/MINOR: tools: do not create an empty arg from trailing spaces
- MEDIUM: config: warn about the consequences of empty arguments on a config line
- MINOR: tools: make parse_line() provide hints about empty args
- MINOR: cfgparse: visually show the input line on empty args
- BUG/MINOR: tools: always terminate empty lines
- BUG/MINOR: tools: make parseline report the required space for the trailing 0
- DEBUG: threads: don't keep lock label "OTHER" in the per-thread history
- DEBUG: threads: merge successive idempotent lock operations in history
- DEBUG: threads: display held locks in threads dumps
- BUG/MINOR: proxy: only use proxy_inc_fe_cum_sess_ver_ctr() with frontends
- Revert "BUG/MEDIUM: mux-spop: Handle CLOSING state and wait for AGENT DISCONNECT frame"
- MINOR: acme/cli: 'acme status' show the status acme-configured certificates
- MEDIUM: acme/ssl: remove 'acme ps' in favor of 'acme status'
- DOC: configuration: add "acme" section to the keywords list
- DOC: configuration: add the "crt-store" keyword
- BUG/MAJOR: queue: lock around the call to pendconn_process_next_strm()
- MINOR: ssl: add filename and linenum for ssl-f-use errors
- BUG/MINOR: ssl: can't use crt-store some certificates in ssl-f-use
- BUG/MINOR: tools: only fill first empty arg when not out of range
- MINOR: debug: bump the dump buffer to 8kB
- MINOR: stick-tables: add "ipv4" as an alias for the "ip" type
- MINOR: quic: extend return value during TP parsing
- BUG/MINOR: quic: use proper error code on missing CID in TPs
- BUG/MINOR: quic: use proper error code on invalid server TP
- BUG/MINOR: quic: reject retry_source_cid TP on server side
- BUG/MINOR: quic: use proper error code on invalid received TP value
- BUG/MINOR: quic: fix TP reject on invalid max-ack-delay
- BUG/MINOR: quic: reject invalid max_udp_payload size
- BUG/MEDIUM: peers: hold the refcnt until updating ts->seen
- BUG/MEDIUM: stick-tables: close a tiny race in __stksess_kill()
- BUG/MINOR: cli: fix too many args detection for commands
- MINOR: server: ensure server postparse tasks are run for dynamic servers
- BUG/MEDIUM: stick-table: always remove update before adding a new one
- BUG/MEDIUM: quic: free stream_desc on all data acked
- BUG/MINOR: cfgparse: consider the special case of empty arg caused by \x00
- DOC: config: recommend disabling libc-based resolution with resolvers
2025/05/02 : 3.2-dev14
- MINOR: acme: retry label always do a request
- MINOR: acme: does not leave task for next request
- BUG/MINOR: acme: reinit the retries only at next request
- MINOR: acme: change the default max retries to 5
- MINOR: acme: allow a delay after a valid response
- MINOR: acme: wait 5s before checking the challenges results
- MINOR: acme: emit a log when starting
- MINOR: acme: delay of 5s after the finalize
- BUG/MEDIUM: quic: Let it be known if the tasklet has been released.
- BUG/MAJOR: tasks: fix task accounting when killed
- CLEANUP: tasks: use the local state, not t->state, to check for tasklets
- DOC: acme: external account binding is not supported
- MINOR: hlua: ignore "tune.lua.bool-sample-conversion" if set after "lua-load"
- MEDIUM: peers: Give up if we fail to take locks in hot path
- MEDIUM: stick-tables: defer adding updates to a tasklet
- MEDIUM: stick-tables: Limit the number of old entries we remove
- MEDIUM: stick-tables: Limit the number of entries we expire
- MINOR: cfgparse-global: add explicit error messages in cfg_parse_global_env_opts
- MINOR: ssl: add function to extract X509 notBefore date in time_t
- BUILD: acme: need HAVE_ASN1_TIME_TO_TM
- MINOR: acme: move the acme task init in a dedicated function
- MEDIUM: acme: add a basic scheduler
- MINOR: acme: emit a log when the scheduler can't start the task
2025/04/30 : 3.2-dev13
- MEDIUM: checks: Make sure we return the tasklet from srv_chk_io_cb
- MEDIUM: listener: Make sure w ereturn the tasklet from accept_queue_process
- MEDIUM: mux_fcgi: Make sure we return the tasklet from fcgi_deferred_shut
- MEDIUM: quic: Make sure we return the tasklet from qcc_io_cb
- MEDIUM: quic: Make sure we return NULL in quic_conn_app_io_cb if needed
- MEDIUM: quic: Make sure we return the tasklet from quic_accept_run
- BUG/MAJOR: tasklets: Make sure he tasklet can't run twice
- BUG/MAJOR: listeners: transfer connection accounting when switching listeners
- MINOR: ssl/cli: add a '-t' option to 'show ssl sni'
- DOC: config: fix ACME paragraph rendering issue
- DOC: config: clarify log-forward "host" option
- MINOR: promex: expose ST_I_PX_RATE (current_session_rate)
- BUILD: acme: use my_strndup() instead of strndup()
- BUILD: leastconn: fix build warning when building without threads on old machines
- MINOR: threads: prepare DEBUG_THREAD to receive more values
- MINOR: threads: turn the full lock debugging to DEBUG_THREAD=2
- MEDIUM: threads: keep history of taken locks with DEBUG_THREAD > 0
- MINOR: threads/cli: display the lock history on "show threads"
- MEDIUM: thread: set DEBUG_THREAD to 1 by default
- BUG/MINOR: ssl/acme: free EVP_PKEY upon error
- MINOR: acme: separate the code generating private keys
- MINOR: acme: failure when no directory is specified
- MEDIUM: acme: generate the account file when not found
- MEDIUM: acme: use 'crt-base' to load the account key
- MINOR: compiler: add more macros to detect macro definitions
- MINOR: cli: split APPCTX_CLI_ST1_PROMPT into two distinct flags
- MEDIUM: cli: make the prompt mode configurable between n/i/p
- MEDIUM: mcli: make the prompt mode configurable between i/p
- MEDIUM: mcli: replicate the current mode when enterin the worker process
- DOC: configuration: acme account key are auto generated
- CLEANUP: acme: remove old TODO for account key
- DOC: configuration: add quic4 to the ssl-f-use example
- BUG/MINOR: acme: does not try to unlock after a failed trylock
- BUG/MINOR: mux-h2: fix the offset of the pattern for the ping frame
- MINOR: tcp: add support for setting TCP_NOTSENT_LOWAT on both sides
- BUG/MINOR: acme: creating an account should not end the task
- MINOR: quic: rename min/max fields for congestion window algo
- MINOR: quic: refactor BBR API
- BUG/MINOR: quic: ensure cwnd limits are always enforced
- MINOR: thread: define cshared type
- MINOR: quic: account for global congestion window
- MEDIUM: quic: limit global Tx memory
- MEDIUM: acme: use a map to store tokens and thumbprints
- BUG/MINOR: acme: remove references to virt@acme
- MINOR: applet: add appctx_schedule() macro
- BUG/MINOR: dns: add tempo between 2 connection attempts for dns servers
- CLEANUP: dns: remove unused dns_stream_server struct member
- BUG/MINOR: dns: prevent ds accumulation within dss
- CLEANUP: proxy: mention that px->conn_retries isn't relevant in some cases
- DOC: ring: refer to newer RFC5424
- MINOR: tools: make my_strndup() take a size_t len instead of and int
- MINOR: Add "sigalg" to "sigalg name" helper function
- MINOR: ssl: Add traces to ssl init/close functions
- MINOR: ssl: Add traces to recv/send functions
- MINOR: ssl: Add traces to ssl_sock_io_cb function
- MINOR: ssl: Add traces around SSL_do_handshake call
- MINOR: ssl: Add traces to verify callback
- MINOR: ssl: Add ocsp stapling callback traces
- MINOR: ssl: Add traces to the switchctx callback
- MINOR: ssl: Add traces about sigalg extension parsing in clientHello callback
- MINOR: Add 'conn' param to ssl_sock_chose_sni_ctx
- BUG/MEDIUM: mux-spop: Wait end of handshake to declare a spop connection ready
- BUG/MEDIUM: mux-spop: Handle CLOSING state and wait for AGENT DISCONNECT frame
- BUG/MINOR: mux-h1: Don't pretend connection was released for TCP>H1>H2 upgrade
- BUG/MINOR: mux-h1: Fix trace message in h1_detroy() to not relay on connection
- BUILD: ssl: Fix wolfssl build
- BUG/MINOR: mux-spop: Use the right bitwise operator in spop_ctl()
- MEDIUM: mux-quic: increase flow-control on each bufsize
- MINOR: mux-quic: limit emitted MSD frames count per qcs
- MINOR: add hlua_yield_asap() helper
- MINOR: hlua_fcn: enforce yield after *_get_stats() methods
- DOC: config: restore default values for resolvers hold directive
- MINOR: ssl/cli: "acme ps" shows the acme tasks
- MINOR: acme: acme_ctx_destroy() returns upon NULL
- MINOR: acme: use acme_ctx_destroy() upon error
- MEDIUM: tasks: Mutualize code between tasks and tasklets.
- MEDIUM: tasks: More code factorization
- MEDIUM: tasks: Remove TASK_IN_LIST and use TASK_QUEUED instead.
- MINOR: tasks: Remove unused tasklet_remove_from_tasklet_list
- MEDIUM: tasks: Mutualize the TASK_KILLED code between tasks and tasklets
- BUG/MEDIUM: connections: Report connection closing in conn_create_mux()
- BUILD/MEDIUM: quic: Make sure we build with recent changes
2025/04/25 : 3.2-dev12
- BUG/MINOR: quic: do not crash on CRYPTO ncbuf alloc failure
- BUG/MINOR: proxy: always detach a proxy from the names tree on free()
- CLEANUP: proxy: detach the name node in proxy_free_common() instead
- CLEANUP: Slightly reorder some proxy option flags to free slots
- MINOR: proxy: Add options to drop HTTP trailers during message forwarding
- MINOR: h1-htx: Skip C-L and T-E headers for 1xx and 204 messages during parsing
- MINOR: mux-h1: Keep custom "Content-Length: 0" header in 1xx and 204 messages
- MINOR: hlua/h1: Use http_parse_cont_len_header() to parse content-length value
- CLEANUP: h1: Remove now useless h1_parse_cont_len_header() function
- BUG/MEDIUM: mux-spop: Respect the negociated max-frame-size value to send frames
- MINOR: http-act: Add 'pause' action to temporarily suspend the message analysis
- MINOR: acme/cli: add the 'acme renew' command to the help message
- MINOR: httpclient: add an "https" log-format
- MEDIUM: acme: use a customized proxy
- MEDIUM: acme: rename "uri" into "directory"
- MEDIUM: acme: rename "account" into "account-key"
- MINOR: stick-table: use a separate lock label for updates
- MINOR: h3: simplify h3_rcv_buf return path
- BUG/MINOR: mux-quic: fix possible infinite loop during decoding
- BUG/MINOR: mux-quic: do not decode if conn in error
- BUG/MINOR: cli: Issue an error when too many args are passed for a command
- MINOR: cli: Use a full prompt command for bidir connections with workers
- MAJOR: cli: Refacor parsing and execution of pipelined commands
- MINOR: cli: Rename some CLI applet states to reflect recent refactoring
- CLEANUP: applet: Update st0/st1 comment in appctx structure
- BUG/MINOR: hlua: Fix I/O handler of lua CLI commands to not rely on the SC
- BUG/MINOR: ring: Fix I/O handler of "show event" command to not rely on the SC
- MINOR: cli/applet: Move appctx fields only used by the CLI in a private context
- MINOR: cache: Add a pointer on the cache in the cache applet context
- MINOR: hlua: Use the applet name in error messages for lua services
- MINOR: applet: Save the "use-service" rule in the stream to init a service applet
- CLEANUP: applet: Remove unsued rule pointer in appctx structure
- BUG/MINOR: master/cli: properly trim the '@@' process name in error messages
- MEDIUM: resolvers: add global "dns-accept-family" directive
- MINOR: resolvers: add command-line argument -4 to force IPv4-only DNS
- MINOR: sock-inet: detect apparent IPv6 connectivity
- MINOR: resolvers: add "dns-accept-family auto" to rely on detected IPv6
- MEDIUM: acme: use Retry-After value for retries
- MEDIUM: acme: reset the remaining retries
- MEDIUM: acme: better error/retry management of the challenge checks
- BUG/MEDIUM: cli: Handle applet shutdown when waiting for a command line
- Revert "BUG/MINOR: master/cli: properly trim the '@@' process name in error messages"
- BUG/MINOR: master/cli: only parse the '@@' prefix on complete lines
- MINOR: resolvers: use the runtime IPv6 status instead of boot time one
2025/04/18 : 3.2-dev11
- CI: enable weekly QuicTLS build
- DOC: management: slightly clarify the prefix role of the '@' command
- DOC: management: add a paragraph about the limitations of the '@' prefix
- MINOR: master/cli: support bidirectional communications with workers
- MEDIUM: ssl/ckch: add filename and linenum argument to crt-store parsing
- MINOR: acme: add the acme section in the configuration parser
- MINOR: acme: add configuration for the crt-store
- MINOR: acme: add private key configuration
- MINOR: acme/cli: add the 'acme renew' command
- MINOR: acme: the acme section is experimental
- MINOR: acme: get the ACME directory
- MINOR: acme: handle the nonce
- MINOR: acme: check if the account exist
- MINOR: acme: generate new account
- MINOR: acme: newOrder request retrieve authorizations URLs
- MINOR: acme: allow empty payload in acme_jws_payload()
- MINOR: acme: get the challenges object from the Auth URL
- MINOR: acme: send the request for challenge ready
- MINOR: acme: implement a check on the challenge status
- MINOR: acme: generate the CSR in a X509_REQ
- MINOR: acme: finalize by sending the CSR
- MINOR: acme: verify the order status once finalized
- MINOR: acme: implement retrieval of the certificate
- BUG/MINOR: acme: ckch_conf_acme_init() when no filename
- MINOR: ssl/ckch: handle ckch_conf in ckchs_dup() and ckch_conf_clean()
- MINOR: acme: copy the original ckch_store
- MEDIUM: acme: replace the previous ckch instance with new ones
- MINOR: acme: schedule retries with a timer
- BUILD: acme: enable the ACME feature when JWS is present
- BUG/MINOR: cpu-topo: check the correct variable for NULL after malloc()
- BUG/MINOR: acme: key not restored upon error in acme_res_certificate()
- BUG/MINOR: thread: protect thread_cpus_enabled_at_boot with USE_THREAD
- MINOR: acme: default to 2048bits for RSA
- DOC: acme: explain how to configure and run ACME
- BUG/MINOR: debug: remove the trailing \n from BUG_ON() statements
- DOC: config: add the missing "profiling.memory" to the global kw index
- DOC: config: add the missing "force-cfg-parser-pause" to the global kw index
- DEBUG: init: report invalid characters in debug description strings
- DEBUG: rename DEBUG_GLITCHES to DEBUG_COUNTERS and enable it by default
- DEBUG: counters: make COUNT_IF() only appear at DEBUG_COUNTERS>=1
- DEBUG: counters: add the ability to enable/disable updating the COUNT_IF counters
- MINOR: tools: let dump_addr_and_bytes() support dumping before the offset
- MINOR: debug: in call traces, dump the 8 bytes before the return address, not after
- MINOR: debug: detect call instructions and show the branch target in backtraces
- BUG/MINOR: acme: fix possible NULL deref
- CLEANUP: acme: stored value is overwritten before it can be used
- BUILD: incompatible pointer type suspected with -DDEBUG_UNIT
- BUG/MINOR: http-ana: Properly detect client abort when forwarding the response
- BUG/MEDIUM: http-ana: Report 502 from req analyzer only during rsp forwarding
- CI: fedora rawhide: enable unit tests
- DOC: configuration: fix a typo in ACME documentation
- MEDIUM: sink: add a new dpapi ring buffer
- Revert "BUG/MINOR: acme: key not restored upon error in acme_res_certificate()"
- BUG/MINOR: acme: key not restored upon error in acme_res_certificate() V2
- BUG/MINOR: acme: fix the exponential backoff of retries
- DOC: configuration: specify limitations of ACME for 3.2
- MINOR: acme: emit logs instead of ha_notice
- MINOR: acme: add a success message to the logs
- BUG/MINOR: acme/cli: fix certificate name in error message
- MINOR: acme: register the task in the ckch_store
- MINOR: acme: free acme_ctx once the task is done
- BUG/MEDIUM: h3: trim whitespaces when parsing headers value
- BUG/MEDIUM: h3: trim whitespaces in header value prior to QPACK encoding
- BUG/MINOR: h3: filter upgrade connection header
- BUG/MINOR: h3: reject invalid :path in request
- BUG/MINOR: h3: reject request URI with invalid characters
- MEDIUM: h3: use absolute URI form with :authority
- BUG/MEDIUM: hlua: fix hlua_applet_{http,tcp}_fct() yield regression (lost data)
- BUG/MINOR: mux-h2: prevent past scheduling with idle connections
- BUG/MINOR: rhttp: fix reconnect if timeout connect unset
- BUG/MINOR: rhttp: ensure GOAWAY can be emitted after reversal
- BUG/MINOR: mux-h2: do not apply timer on idle backend connection
- MINOR: mux-h2: refactor idle timeout calculation
- MINOR: mux-h2: prepare to support PING emission
- MEDIUM: server/mux-h2: implement idle-ping on backend side
- MEDIUM: listener/mux-h2: implement idle-ping on frontend side
- MINOR: mux-h2: do not emit GOAWAY on idle ping expiration
- MINOR: mux-h2: handle idle-ping on conn reverse
- BUILD: makefile: enable backtrace by default on musl
- BUG/MINOR: threads: set threads_idle and threads_harmless even with no threads
- BUG/MINOR debug: fix !USE_THREAD_DUMP in ha_thread_dump_fill()
- BUG/MINOR: wdt/debug: avoid signal re-entrance between debugger and watchdog
- BUG/MINOR: debug: detect and prevent re-entrance in ha_thread_dump_fill()
- MINOR: debug: do not statify a few debugging functions often used with wdt/dbg
- MINOR: tools: also protect the library name resolution against concurrent accesses
- MINOR: tools: protect dladdr() against reentrant calls from the debug handler
- MINOR: debug: protect ha_dump_backtrace() against risks of re-entrance
- MINOR: tinfo: keep a copy of the pointer to the thread dump buffer
- MINOR: debug: always reset the dump pointer when done
- MINOR: debug: remove unused case of thr!=tid in ha_thread_dump_one()
- MINOR: pass a valid buffer pointer to ha_thread_dump_one()
- MEDIUM: wdt: always make the faulty thread report its own warnings
- MINOR: debug: make ha_stuck_warning() only work for the current thread
- MINOR: debug: make ha_stuck_warning() print the whole message at once
- CLEANUP: debug: no longer set nor use TH_FL_DUMPING_OTHERS
- MINOR: sched: add a new function is_sched_alive() to report scheduler's health
- MINOR: wdt: use is_sched_alive() instead of keeping a local ctxsw copy
- MINOR: sample: add 4 new sample fetches for clienthello parsing
- REGTEST: add new reg-test for the 4 new clienthello fetches
- MINOR: servers: Move the per-thread server initialization earlier
- MINOR: proxies: Initialize the per-thread structure earlier.
- MINOR: servers: Provide a pointer to the server in srv_per_tgroup.
- MINOR: lb_fwrr: Move the next weight out of fwrr_group.
- MINOR: proxies: Add a per-thread group lbprm struct.
- MEDIUM: lb_fwrr: Use one ebtree per thread group.
- MEDIUM: lb_fwrr: Don't start all thread groups on the same server.
- MINOR: proxies: Do stage2 initialization for sinks too
2025/04/11 : 3.2-dev10
- REORG: ssl: move curves2nid and nid2nist to ssl_utils
- BUG/MEDIUM: stream: Fix a possible freeze during a forced shut on a stream
- MEDIUM: stream: Save SC and channel flags earlier in process_steam()
- BUG/MINOR: peers: fix expire learned from a peer not converted from ms to ticks
- BUG/MEDIUM: peers: prevent learning expiration too far in futur from unsync node
- CI: spell check: allow manual trigger
- CI: codespell: add "pres" to spellcheck whitelist
- CLEANUP: assorted typo fixes in the code, commits and doc
- CLEANUP: atomics: remove support for gcc < 4.7
- CLEANUP: atomics: also replace __sync_synchronize() with __atomic_thread_fence()
- TESTS: Fix build for filltab25.c
- MEDIUM: ssl: replace "crt" lines by "ssl-f-use" lines
- DOC: configuration: replace "crt" by "ssl-f-use" in listeners
- MINOR: backend: mark srv as nonnull in alloc_dst_address()
- BUG/MINOR: server: ensure check-reuse-pool is copied from default-server
- MINOR: server: activate automatically check reuse for rhttp@ protocol
- MINOR: check/backend: support conn reuse with SNI
- MINOR: check: implement check-pool-conn-name srv keyword
- MINOR: task: add thread safe notification_new and notification_wake variants
- BUG/MINOR: hlua_fcn: fix potential UAF with Queue:pop_wait()
- MINOR: hlua_fcn: register queue class using hlua_register_metatable()
- MINOR: hlua: add core.wait()
- MINOR: hlua: core.wait() takes optional delay paramater
- MINOR: hlua: split hlua_applet_tcp_recv_yield() in two functions
- MINOR: hlua: add AppletTCP:try_receive()
- MINOR: hlua_fcn: add Queue:alarm()
- MEDIUM: task: make notification_* API thread safe by default
- CLEANUP: log: adjust _lf_cbor_encode_byte() comment
- MEDIUM: ssl/crt-list: warn on negative wildcard filters
- MEDIUM: ssl/crt-list: warn on negative filters only
- BUILD: atomics: fix build issue on non-x86/non-arm systems
- BUG/MINOR: log: fix CBOR encoding with LOG_VARTEXT_START() + lf_encode_chunk()
- BUG/MEDIUM: sample: fix risk of overflow when replacing multiple regex back-refs
- DOC: configuration: rework the crt-list section
- MINOR: ring: support arbitrary delimiters through ring_dispatch_messages()
- MINOR: ring/cli: support delimiting events with a trailing \0 on "show events"
- DEV: h2: fix h2-tracer.lua nil value index
- BUG/MINOR: backend: do not use the source port when hashing clientip
- BUG/MINOR: hlua: fix invalid errmsg use in hlua_init()
- MINOR: proxy: add setup_new_proxy() function
- MINOR: checks: mark CHECKS-FE dummy frontend as internal
- MINOR: flt_spoe: mark spoe agent frontend as internal
- MEDIUM: tree-wide: avoid manually initializing proxies
- MINOR: proxy: add deinit_proxy() helper func
- MINOR: checks: deinit checks_fe upon deinit
- MINOR: flt_spoe: deinit spoe agent proxy upon agent release
2025/04/02 : 3.2-dev9
- MINOR: quic: move global tune options into quic_tune
- CLEANUP: quic: reorganize TP flow-control initialization
- MINOR: quic: ignore uni-stream for initial max data TP
- MINOR: mux-quic: define config for max-data
- MINOR: quic: define max-stream-data configuration as a ratio
- MEDIUM: lb-chash: add directive hash-preserve-affinity
- MEDIUM: pools: be a bit smarter when merging comparable size pools
- REGTESTS: disable the test balance/balance-hash-maxqueue
- BUG/MINOR: log: fix gcc warn about truncating NUL terminator while init char arrays
- CI: fedora rawhide: allow "on: workflow_dispatch" in forks
- CI: fedora rawhide: install "awk" as a dependency
- CI: spellcheck: allow "on: workflow_dispatch" in forks
- CI: coverity scan: allow "on: workflow_dispatch" in forks
- CI: cross compile: allow "on: workflow_dispatch" in forks
- CI: Illumos: allow "on: workflow_dispatch" in forks
- CI: NetBSD: allow "on: workflow_dispatch" in forks
- CI: QUIC Interop on AWS-LC: allow "on: workflow_dispatch" in forks
- CI: QUIC Interop on LibreSSL: allow "on: workflow_dispatch" in forks
- MINOR: compiler: add __nonstring macro
- MINOR: thread: dump the CPU topology in thread_map_to_groups()
- MINOR: cpu-set: compare two cpu sets with ha_cpuset_isequal()
- MINOR: cpu-set: add a new function to print cpu-sets in human-friendly mode
- MINOR: cpu-topo: add a dump of thread-to-CPU mapping to -dc
- MINOR: cpu-topo: pass an extra argument to ha_cpu_policy
- MINOR: cpu-topo: add new cpu-policies "group-by-2-clusters" and above
- BUG/MINOR: config: silence .notice/.warning/.alert in discovery mode
- EXAMPLES: add "games.cfg" and an example game in Lua
- MINOR: jws: emit the JWK thumbprint
- TESTS: jws: change the jwk format
- MINOR: ssl/ckch: add substring parser for ckch_conf
- MINOR: mt_list: Implement mt_list_try_lock_prev().
- MINOR: lbprm: Add method to deinit server and proxy
- MINOR: threads: Add HA_RWLOCK_TRYRDTOWR()
- MAJOR: leastconn; Revamp the way servers are ordered.
- BUG/MINOR: ssl/ckch: leak in error path
- BUILD: ssl/ckch: potential null pointer dereference
- MINOR: log: support "raw" logformat node typecast
- CLEANUP: assorted typo fixes in the code and comments
- DOC: config: fix two missing "content" in "tcp-request" examples
- MINOR: cpu-topo: cpu_dump_topology() SMT info check little optimisation
- BUILD: compiler: undefine the CONCAT() macro if already defined
- BUG/MEDIUM: leastconn: Don't try to reposition if the server is down
- BUG/MINOR: rhttp: fix incorrect dst/dst_port values
- BUG/MINOR: backend: do not overwrite srv dst address on reuse
- BUG/MEDIUM: backend: fix reuse with set-dst/set-dst-port
- MINOR: sample: define bc_reused fetch
- REGTESTS: extend conn reuse test with transparent proxy
- MINOR: backend: fix comment when killing idle conns
- MINOR: backend: adjust conn_backend_get() API
- MINOR: backend: extract conn hash calculation from connect_server()
- MINOR: backend: extract conn reuse from connect_server()
- MINOR: backend: remove stream usage on connection reuse
- MINOR: check define check-reuse-pool server keyword
- MEDIUM: check: implement check-reuse-pool
- BUILD: backend: silence a build warning when not using ssl
- BUILD: quic_sock: address a strict-aliasing build warning with gcc 5 and 6
- BUILD: ssl_ckch: use my_strndup() instead of strndup()
- DOC: update INSTALL to reflect the minimum compiler version
2025/03/21 : 3.2-dev8
- MINOR: jws: implement JWS signing
- TESTS: jws: implement a test for JWS signing
- CI: github: add "jose" to apt dependencies
- CLEANUP: log-forward: remove useless options2 init
- CLEANUP: log: add syslog_process_message() helper
- MINOR: proxy: add proxy->options3
- MINOR: log: migrate log-forward options from proxy->options2 to options3
- MINOR: log: provide source address information in syslog_process_message()
- MINOR: tools: only print address in sa2str() when port == -1
- MINOR: log: add "option host" log-forward option
- MINOR: log: handle log-forward "option host"
- MEDIUM: log: change default "host" strategy for log-forward section
- BUG/MEDIUM: thread: use pthread_self() not ha_pthread[tid] in set_affinity
- MINOR: compiler: add a simple macro to concatenate resolved strings
- MINOR: compiler: add a new __decl_thread_var() macro to declare local variables
- BUILD: tools: silence a build warning when USE_THREAD=0
- BUILD: backend: silence a build warning when threads are disabled
- DOC: management: rename some last occurences from domain "dns" to "resolvers"
- BUG/MINOR: stats: fix capabilities and hide settings for some generic metrics
- MINOR: cli: export cli_io_handler() to ease symbol resolution
- MINOR: tools: improve symbol resolution without dl_addr
- MINOR: tools: ease the declaration of known symbols in resolve_sym_name()
- MINOR: tools: teach resolve_sym_name() a few more common symbols
- BUILD: tools: avoid a build warning on gcc-4.8 in resolve_sym_name()
- DEV: ncpu: also emulate sysconf() for _SC_NPROCESSORS_*
- DOC: design-thoughts: commit numa-auto.txt
- MINOR: cpuset: make the API support negative CPU IDs
- MINOR: thread: rely on the cpuset functions to count bound CPUs
- MINOR: cpu-topo: add ha_cpu_topo definition
- MINOR: cpu-topo: allocate and initialize the ha_cpu_topo array.
- MINOR: cpu-topo: rely on _SC_NPROCESSORS_CONF to trim maxcpus
- MINOR: cpu-topo: add a function to dump CPU topology
- MINOR: cpu-topo: update CPU topology from excluded CPUs at boot
- REORG: cpu-topo: move bound cpu detection from cpuset to cpu-topo
- MINOR: cpu-topo: add detection of online CPUs on Linux
- MINOR: cpu-topo: add detection of online CPUs on FreeBSD
- MINOR: cpu-topo: try to detect offline cpus at boot
- MINOR: cpu-topo: add CPU topology detection for linux
- MINOR: cpu-topo: also store the sibling ID with SMT
- MINOR: cpu-topo: add NUMA node identification to CPUs on Linux
- MINOR: cpu-topo: add NUMA node identification to CPUs on FreeBSD
- MINOR: thread: turn thread_cpu_mask_forced() into an init-time variable
- MINOR: cfgparse: move the binding detection into numa_detect_topology()
- MINOR: cfgparse: use already known offline CPU information
- MINOR: global: add a command-line option to enable CPU binding debugging
- MINOR: cpu-topo: add a new "cpu-set" global directive to choose cpus
- MINOR: cpu-topo: add "drop-cpu" and "only-cpu" to cpu-set
- MEDIUM: thread: start to detect thread groups and threads min/max
- MEDIUM: cpu-topo: make sure to properly assign CPUs to threads as a fallback
- MEDIUM: thread: reimplement first numa node detection
- MEDIUM: cfgparse: remove now unused numa & thread-count detection
- MINOR: cpu-topo: refine cpu dump output to better show kept/dropped CPUs
- MINOR: cpu-topo: fall back to nominal_perf and scaling_max_freq for the capacity
- MINOR: cpu-topo: use cpufreq before acpi cppc
- MINOR: cpu-topo: boost the capacity of performance cores with cpufreq
- MINOR: cpu-topo: skip CPU detection when /sys/.../cpu does not exist
- MINOR: cpu-topo: skip identification of non-existing CPUs
- MINOR: cpu-topo: skip CPU properties that we've verified do not exist
- MINOR: cpu-topo: implement a sorting mechanism for CPU index
- MINOR: cpu-topo: implement a sorting mechanism by CPU locality
- MINOR: cpu-topo: implement a CPU sorting mechanism by cluster ID
- MINOR: cpu-topo: ignore single-core clusters
- MINOR: cpu-topo: assign clusters to cores without and renumber them
- MINOR: cpu-topo: make sure we don't leave unassigned IDs in the cpu_topo
- MINOR: cpu-topo: assign an L3 cache if more than 2 L2 instances
- MINOR: cpu-topo: renumber cores to avoid holes and make them contiguous
- MINOR: cpu-topo: add a function to sort by cluster+capacity
- MINOR: cpu-topo: consider capacity when forming clusters
- MINOR: cpu-topo: create an array of the clusters
- MINOR: cpu-topo: ignore excess of too small clusters
- MINOR: cpu-topo: add "only-node" and "drop-node" to cpu-set
- MINOR: cpu-topo: add "only-thread" and "drop-thread" to cpu-set
- MINOR: cpu-topo: add "only-core" and "drop-core" to cpu-set
- MINOR: cpu-topo: add "only-cluster" and "drop-cluster" to cpu-set
- MINOR: cpu-topo: add a CPU policy setting to the global section
- MINOR: cpu-topo: add a 'first-usable-node' cpu policy
- MEDIUM: cpu-topo: use the "first-usable-node" cpu-policy by default
- CLEANUP: thread: now remove the temporary CPU node binding code
- MINOR: cpu-topo: add cpu-policy "group-by-cluster"
- MEDIUM: cpu-topo: let the "group-by-cluster" split groups
- MINOR: cpu-topo: add a new "performance" cpu-policy
- MINOR: cpu-topo: add a new "efficiency" cpu-policy
- MINOR: cpu-topo: add a new "resource" cpu-policy
- MINOR: jws: add new functions in jws.h
- MINOR: cpu-topo: fix unused stack var 'cpu2' reported by coverity
- MINOR: hlua: add an optional timeout to AppletTCP:receive()
- MINOR: jws: use jwt_alg type instead of a char
- BUG/MINOR: log: prevent saddr NULL deref in syslog_io_handler()
- MINOR: stream: decrement srv->served after detaching from the list
- BUG/MINOR: hlua: fix optional timeout argument index for AppletTCP:receive()
- MINOR: server: simplify srv_has_streams()
- CLEANUP: server: make it clear that srv_check_for_deletion() is thread-safe
- MINOR: cli/server: don't take thread isolation to check for srv-removable
- BUG/MINOR: limits: compute_ideal_maxconn: don't cap remain if fd_hard_limit=0
- MINOR: limits: fix check_if_maxsock_permitted description
- BUG/MEDIUM: hlua/cli: fix cli applet UAF in hlua_applet_wakeup()
- MINOR: tools: path_base() concatenates a path with a base path
- MEDIUM: ssl/ckch: make the ckch_conf more generic
- BUG/MINOR: mux-h2: Reset streams with NO_ERROR code if full response was already sent
- MINOR: stats: add .generic explicit field in stat_col struct
- MINOR: stats: STATS_PX_CAP___B_ macro
- MINOR: stats: add .cap for some static metrics
- MINOR: stats: use stat_col storage stat_cols_info
- MEDIUM: promex: switch to using stat_cols_info for global metrics
- MINOR: promex: expose ST_I_INF_WARNINGS (AKA total_warnings) metric
- MEDIUM: promex: switch to using stat_cols_px for front/back/server metrics
- MINOR: stats: explicitly add frontend cap for ST_I_PX_REQ_TOT
- CLEANUP: promex: remove unused PROMEX_FL_{INFO,FRONT,BACK,LI,SRV} flags
- BUG/MEDIUM: mux-quic: fix crash on RS/SS emission if already close local
- BUG/MINOR: mux-quic: remove extra BUG_ON() in _qcc_send_stream()
- MEDIUM: mt_list: Reduce the max number of loops with exponential backoff
- MINOR: stats: add alt_name field to stat_col struct
- MINOR: stats: add alt name info to stat_cols_info where relevant
- MINOR: promex: get rid of promex_global_metric array
- MINOR: stats-proxy: add alt_name field for ME_NEW_{FE,BE,PX} helpers
- MINOR: stats-proxy: add alt name info to stat_cols_px where relevant
- MINOR: promex: get rid of promex_st_metrics array
- MINOR: pools: rename the "by_what" field of the show pools context to "how"
- MINOR: cli/pools: record the list of pool registrations even when merging them
2025/03/07 : 3.2-dev7
- BUG/MEDIUM: applet: Don't handle EOI/EOS/ERROR is applet is waiting for room
- BUG/MEDIUM: spoe/mux-spop: Introduce an NOOP action to deal with empty ACK
- BUG/MINOR: cfgparse: fix NULL ptr dereference in cfg_parse_peers
- BUG/MEDIUM: uxst: fix outgoing abns address family in connect()
- REGTESTS: fix reg-tests/server/abnsz.vtc
- BUG/MINOR: log: fix outgoing abns address family
- BUG/MINOR: sink: add tempo between 2 connection attempts for sft servers
- MINOR: clock: always use atomic ops for global_now_ms
- CI: QUIC Interop: clean old docker images
- BUG/MINOR: stream: do not call co_data() from __strm_dump_to_buffer()
- BUG/MINOR: mux-h1: always make sure h1s->sd exists in h1_dump_h1s_info()
- MINOR: tinfo: add a new thread flag to indicate a call from a sig handler
- BUG/MEDIUM: stream: never allocate connection addresses from signal handler
- MINOR: freq_ctr: provide non-blocking read functions
- BUG/MEDIUM: stream: use non-blocking freq_ctr calls from the stream dumper
- MINOR: tools: use only opportunistic symbols resolution
- CLEANUP: task: move the barrier after clearing th_ctx->current
- MINOR: compression: Introduce minimum size
- BUG/MINOR: h2: always trim leading and trailing LWS in header values
- MINOR: tinfo: split the signal handler report flags into 3
- BUG/MEDIUM: stream: don't use localtime in dumps from a signal handler
- OPTIM: connection: don't try to kill other threads' connection when !shared
- BUILD: add possibility to use different QuicTLS variants
- MEDIUM: fd: Wait if locked in fd_grab_tgid() and fd_take_tgid().
- MINOR: fd: Add fd_lock_tgid_cur().
- MEDIUM: epoll: Make sure we can add a new event
- MINOR: pollers: Add a fixup_tgid_takeover() method.
- MEDIUM: pollers: Drop fd events after a takeover to another tgid.
- MEDIUM: connections: Allow taking over connections from other tgroups.
- MEDIUM: servers: Add strict-maxconn.
- BUG/MEDIUM: server: properly initialize PROXY v2 TLVs
- BUG/MINOR: server: fix the "server-template" prefix memory leak
- BUG/MINOR: h3: do not report transfer as aborted on preemptive response
- CLEANUP: h3: fix documentation of h3_rcv_buf()
- MINOR: hq-interop: properly handle incomplete request
- BUG/MEDIUM: mux-fcgi: Try to fully fill demux buffer on receive if not empty
- MINOR: h1: permit to relax the websocket checks for missing mandatory headers
- BUG/MINOR: hq-interop: fix leak in case of rcv_buf early return
- BUG/MINOR: server: check for either proxy-protocol v1 or v2 to send hedaer
- MINOR: jws: implement a JWK public key converter
- DEBUG: init: add a way to register functions for unit tests
- TESTS: add a unit test runner in the Makefile
- TESTS: jws: register a unittest for jwk
- CI: github: run make unit-tests on the CI
- TESTS: add config smoke checks in the unit tests
- MINOR: jws: conversion to NIST curves name
- CI: github: remove smoke tests from vtest.yml
- TESTS: ist: fix wrong array size
- TESTS: ist: use the exit code to return a verdict
- TESTS: ist: add a ist.sh to launch in make unit-tests
- CI: github: fix h2spec.config proxy names
- DEBUG: init: Add a macro to register unit tests
- MINOR: sample: allow custom date format in error-log-format
- CLEANUP: log: removing "log-balance" references
- BUG/MINOR: log: set proper smp size for balance log-hash
- MINOR: log: use __send_log() with exact payload length
- MEDIUM: log: postpone the decision to send or not log with empty messages
- MINOR: proxy: make pr_mode enum bitfield compatible
- MINOR: cfgparse-listen: add and use cfg_parse_listen_match_option() helper
- MINOR: log: add options eval for log-forward
- MINOR: log: detach prepare from parse message
- MINOR: log: add dont-parse-log and assume-rfc6587-ntf options
- BUG/MEIDUM: startup: return to initial cwd only after check_config_validity()
- TESTS: change the output of run-unittests.sh
- TESTS: unit-tests: store sh -x in a result file
- CI: github: show results of the Unit tests
- BUG/MINOR: cfgparse/peers: fix inconsistent check for missing peer server
- BUG/MINOR: cfgparse/peers: properly handle ignored local peer case
- BUG/MINOR: server: dont return immediately from parse_server() when skipping checks
- MINOR: cfgparse/peers: provide more info when ignoring invalid "peer" or "server" lines
- BUG/MINOR: stream: fix age calculation in "show sess" output
- MINOR: stream/cli: rework "show sess" to better consider optional arguments
- MINOR: stream/cli: make "show sess" support filtering on front/back/server
- TESTS: quic: create first quic unittest
- MINOR: h3/hq-interop: restore function for standalone FIN receive
- MINOR/OPTIM: mux-quic: do not allocate rxbuf on standalone FIN
- MINOR: mux-quic: refine reception of standalone STREAM FIN
- MINOR: mux-quic: define globally stream rxbuf size
- MINOR: mux-quic: define rxbuf wrapper
- MINOR: mux-quic: store QCS Rx buf in a single-entry tree
- MINOR: mux-quic: adjust Rx data consumption API
- MINOR: mux-quic: adapt return value of qcc_decode_qcs()
- MAJOR: mux-quic: support multiple QCS RX buffers
- MEDIUM: mux-quic: handle too short data splitted on multiple rxbuf
- MAJOR: mux-quic: increase stream flow-control for multi-buffer alloc
- BUG/MINOR: cfgparse-tcp: relax namespace bind check
- MINOR: startup: adjust alert messages, when capabilities are missed
2025/02/19 : 3.2-dev6
- BUG/MEDIUM: debug: close a possible race between thread dump and panic()
- DEBUG: thread: report the spin lock counters as seek locks
- DEBUG: thread: make lock time computation more consistent
- DEBUG: thread: report the wait time buckets for lock classes
- DEBUG: thread: don't keep the redundant _locked counter
- DEBUG: thread: make lock_stat per operation instead of for all operations
- DEBUG: thread: reduce the struct lock_stat to store only 30 buckets
- MINOR: lbprm: add a new callback ->server_requeue to the lbprm
- MEDIUM: server: allocate a tasklet for asyncronous requeuing
- MAJOR: leastconn: postpone the server's repositioning under contention
- BUG/MINOR: quic: reserve length field for long header encoding
- BUG/MINOR: quic: fix CRYPTO payload size calcul for encoding
- MINOR: quic: simplify length calculation for STREAM/CRYPTO frames
- BUG/MINOR: mworker: section ignored in discovery after a post_section_parser
- BUG/MINOR: mworker: post_section_parser for the last section in discovery
- CLEANUP: mworker: "program" section does not have a post_section_parser anymore
- MEDIUM: initcall: allow to register mutiple post_section_parser per section
- CI: cirrus-ci: bump FreeBSD image to 14-2
- DOC: initcall: name correctly REGISTER_CONFIG_POST_SECTION()
- REGTESTS: stop using truncated.vtc on freebsd
- MINOR: quic: refactor STREAM encoding and splitting
- MINOR: quic: refactor CRYPTO encoding and splitting
- BUG/MEDIUM: fd: mark FD transferred to another process as FD_CLONED
- BUG/MINOR: ssl/cli: "show ssl crt-list" lacks client-sigals
- BUG/MINOR: ssl/cli: "show ssl crt-list" lacks sigals
- MINOR: ssl/cli: display more filenames in 'show ssl cert'
- DOC: watchdog: document the sequence of the watchdog and panic
- MINOR: ssl: store the filenames resulting from a lookup in ckch_conf
- MINOR: startup: allow hap_register_feature() to enable a feature in the list
- MINOR: quic: support frame type as a varint
- BUG/MINOR: startup: leave at first post_section_parser which fails
- BUG/MINOR: startup: hap_register_feature() fix for partial feature name
- BUG/MEDIUM: cli: Be sure to drop all input data in END state
- BUG/MINOR: cli: Wait for the last ACK when FDs are xferred from the old worker
- BUG/MEDIUM: filters: Handle filters registered on data with no payload callback
- BUG/MINOR: fcgi: Don't set the status to 302 if it is already set
- MINOR: ssl/crtlist: split the ckch_conf loading from the crtlist line parsing
- MINOR: ssl/crtlist: handle crt_path == cc->crt in crtlist_load_crt()
- MINOR: ssl/ckch: return from ckch_conf_clean() when conf is NULL
- MEDIUM: ssl/crtlist: "crt" keyword in frontend
- DOC: configuration: document the "crt" frontend keyword
- DEV: h2: add a Lua-based HTTP/2 connection tracer
- BUG/MINOR: quic: prevent crash on conn access after MUX init failure
- BUG/MINOR: mux-quic: prevent crash after MUX init failure
- DEV: h2: fix flags for the continuation frame
- REGTESTS: Fix truncated.vtc to send 0-CRLF
- BUG/MINOR: mux-h2: Properly handle full or truncated HTX messages on shut
- Revert "REGTESTS: stop using truncated.vtc on freebsd"
- MINOR: mux-quic: define a QCC application state member
- MINOR: mux-quic/h3: emit SETTINGS via MUX tasklet handler
- MINOR: mux-quic/h3: support temporary blocking on control stream sending
2025/02/08 : 3.2-dev5
- BUG/MINOR: ssl: put ssl_sock_load_ca under SSL_NO_GENERATE_CERTIFICATES
- CLEANUP: ssl: rename ssl_sock_load_ca to ssl_sock_gencert_load_ca

View File

@ -1010,7 +1010,7 @@ you notice you're already practising some of them:
- continue to send pull requests after having been explained why they are not
welcome.
- give wrong advices to people asking for help, or sending them patches to
- give wrong advice to people asking for help, or sending them patches to
try which make no sense, waste their time, and give them a bad impression
of the people working on the project.

67
INSTALL
View File

@ -111,20 +111,22 @@ HAProxy requires a working GCC or Clang toolchain and GNU make :
may want to retry with "gmake" which is the name commonly used for GNU make
on BSD systems.
- GCC >= 4.2 (up to 14 tested). Older versions can be made to work with a
few minor adaptations if really needed. Newer versions may sometimes break
due to compiler regressions or behaviour changes. The version shipped with
your operating system is very likely to work with no trouble. Clang >= 3.0
is also known to work as an alternative solution. Recent versions may emit
a bit more warnings that are worth reporting as they may reveal real bugs.
TCC (https://repo.or.cz/tinycc.git) is also usable for developers but will
not support threading and was found at least once to produce bad code in
some rare corner cases (since fixed). But it builds extremely quickly
(typically half a second for the whole project) and is very convenient to
run quick tests during API changes or code refactoring.
- GCC >= 4.7 (up to 14 tested). Older versions are no longer supported due to
the latest mt_list update which only uses c11-like atomics. Newer versions
may sometimes break due to compiler regressions or behaviour changes. The
version shipped with your operating system is very likely to work with no
trouble. Clang >= 3.0 is also known to work as an alternative solution, and
versions up to 19 were successfully tested. Recent versions may emit a bit
more warnings that are worth reporting as they may reveal real bugs. TCC
(https://repo.or.cz/tinycc.git) is also usable for developers but will not
support threading and was found at least once to produce bad code in some
rare corner cases (since fixed). But it builds extremely quickly (typically
half a second for the whole project) and is very convenient to run quick
tests during API changes or code refactoring.
- GNU ld (binutils package), with no particular version. Other linkers might
work but were not tested.
work but were not tested. The default one from your operating system will
normally work.
On debian or Ubuntu systems and their derivatives, you may get all these tools
at once by issuing the two following commands :
@ -235,7 +237,7 @@ to forcefully enable it using "USE_LIBCRYPT=1".
-----------------
For SSL/TLS, it is necessary to use a cryptography library. HAProxy currently
supports the OpenSSL library, and is known to build and work with branches
1.0.0, 1.0.1, 1.0.2, 1.1.0, 1.1.1, and 3.0 to 3.4. It is recommended to use
1.0.0, 1.0.1, 1.0.2, 1.1.0, 1.1.1, and 3.0 to 3.5. It is recommended to use
at least OpenSSL 1.1.1 to have support for all SSL keywords and configuration
in HAProxy. OpenSSL follows a long-term support cycle similar to HAProxy's,
and each of the branches above receives its own fixes, without forcing you to
@ -257,10 +259,10 @@ reported to work as well. While there are some efforts from the community to
ensure they work well, OpenSSL remains the primary target and this means that
in case of conflicting choices, OpenSSL support will be favored over other
options. Note that QUIC is not fully supported when haproxy is built with
OpenSSL. In this case, QUICTLS is the preferred alternative. As of writing
this, the QuicTLS project follows OpenSSL very closely and provides update
simultaneously, but being a volunteer-driven project, its long-term future does
not look certain enough to convince operating systems to package it, so it
OpenSSL < 3.5 version. In this case, QUICTLS is the preferred alternative.
As of writing this, the QuicTLS project follows OpenSSL very closely and provides
update simultaneously, but being a volunteer-driven project, its long-term future
does not look certain enough to convince operating systems to package it, so it
needs to be build locally. See the section about QUIC in this document.
A fifth option is wolfSSL (https://github.com/wolfSSL/wolfssl). It is the only
@ -488,8 +490,8 @@ target. Common issues may include:
other supported compatible library.
- many "dereferencing pointer 'sa.985' does break strict-aliasing rules"
=> these warnings happen on old compilers (typically gcc-4.4), and may
safely be ignored; newer ones are better on these.
=> these warnings happen on old compilers (typically gcc before 7.x),
and may safely be ignored; newer ones are better on these.
4.11) QUIC
@ -498,10 +500,11 @@ QUIC is the new transport layer protocol and is required for HTTP/3. This
protocol stack is currently supported as an experimental feature in haproxy on
the frontend side. In order to enable it, use "USE_QUIC=1 USE_OPENSSL=1".
Note that QUIC is not fully supported by the OpenSSL library. Indeed QUIC 0-RTT
cannot be supported by OpenSSL contrary to others libraries with full QUIC
support. The preferred option is to use QUICTLS. This is a fork of OpenSSL with
a QUIC-compatible API. Its repository is available at this location:
Note that QUIC is not always fully supported by the OpenSSL library depending on
its version. Indeed QUIC 0-RTT cannot be supported by OpenSSL for versions before
3.5 contrary to others libraries with full QUIC support. The preferred option is
to use QUICTLS. This is a fork of OpenSSL with a QUIC-compatible API. Its
repository is available at this location:
https://github.com/quictls/openssl
@ -529,14 +532,18 @@ way assuming that wolfSSL was installed in /opt/wolfssl-5.6.0 as shown in 4.5:
SSL_INC=/opt/wolfssl-5.6.0/include SSL_LIB=/opt/wolfssl-5.6.0/lib
LDFLAGS="-Wl,-rpath,/opt/wolfssl-5.6.0/lib"
As last resort, haproxy may be compiled against OpenSSL as follows:
As last resort, haproxy may be compiled against OpenSSL as follows from 3.5
version with 0-RTT support:
$ make TARGET=generic USE_OPENSSL=1 USE_QUIC=1
or as follows for all OpenSSL versions but without O-RTT support:
$ make TARGET=generic USE_OPENSSL=1 USE_QUIC=1 USE_QUIC_OPENSSL_COMPAT=1
Note that QUIC 0-RTT is not supported by haproxy QUIC stack when built against
OpenSSL. In addition to this compilation requirements, the QUIC listener
bindings must be explicitly enabled with a specific QUIC tuning parameter.
(see "limited-quic" global parameter of haproxy Configuration Manual).
In addition to this requirements, the QUIC listener bindings must be explicitly
enabled with a specific QUIC tuning parameter. (see "limited-quic" global
parameter of haproxy Configuration Manual).
5) How to build HAProxy
@ -754,8 +761,8 @@ forced to produce final binaries, and must not be used during bisect sessions,
as it will often lead to the wrong commit.
Examples:
# silence strict-aliasing warnings with old gcc-4.4:
$ make -j$(nproc) TARGET=linux-glibc CC=gcc-44 CFLAGS=-fno-strict-aliasing
# silence strict-aliasing warnings with old gcc-5.5:
$ make -j$(nproc) TARGET=linux-glibc CC=gcc-55 CFLAGS=-fno-strict-aliasing
# disable all warning options:
$ make -j$(nproc) TARGET=linux-glibc CC=mycc WARN_CFLAGS= NOWARN_CFLAGS=

View File

@ -201,6 +201,7 @@ endif
#### May be used to force running a specific set of reg-tests
REG_TEST_FILES =
REG_TEST_SCRIPT=./scripts/run-regtests.sh
UNIT_TEST_SCRIPT=./scripts/run-unittests.sh
#### Standard C definition
# Compiler-specific flags that may be used to set the standard behavior we
@ -260,9 +261,9 @@ endif
# without appearing here. Currently defined DEBUG macros include DEBUG_FULL,
# DEBUG_MEM_STATS, DEBUG_DONT_SHARE_POOLS, DEBUG_FD, DEBUG_POOL_INTEGRITY,
# DEBUG_NO_POOLS, DEBUG_FAIL_ALLOC, DEBUG_STRICT_ACTION=[0-3], DEBUG_HPACK,
# DEBUG_AUTH, DEBUG_SPOE, DEBUG_UAF, DEBUG_THREAD, DEBUG_STRICT, DEBUG_DEV,
# DEBUG_AUTH, DEBUG_SPOE, DEBUG_UAF, DEBUG_THREAD=0-2, DEBUG_STRICT, DEBUG_DEV,
# DEBUG_TASK, DEBUG_MEMORY_POOLS, DEBUG_POOL_TRACING, DEBUG_QPACK, DEBUG_LIST,
# DEBUG_GLITCHES, DEBUG_STRESS.
# DEBUG_COUNTERS=[0-2], DEBUG_STRESS, DEBUG_UNIT.
DEBUG =
#### Trace options
@ -400,7 +401,7 @@ ifeq ($(TARGET),linux-musl)
USE_POLL USE_TPROXY USE_LIBCRYPT USE_DL USE_RT USE_CRYPT_H USE_NETFILTER \
USE_CPU_AFFINITY USE_THREAD USE_EPOLL USE_LINUX_TPROXY USE_LINUX_CAP \
USE_ACCEPT4 USE_LINUX_SPLICE USE_PRCTL USE_THREAD_DUMP USE_NS USE_TFO \
USE_GETADDRINFO)
USE_GETADDRINFO USE_BACKTRACE)
INSTALL = install -v
endif
@ -591,10 +592,12 @@ endif
ifneq ($(USE_BACKTRACE:0=),)
BACKTRACE_LDFLAGS = -Wl,$(if $(EXPORT_SYMBOL),$(EXPORT_SYMBOL),--export-dynamic)
BACKTRACE_CFLAGS = -fno-omit-frame-pointer
endif
ifneq ($(USE_CPU_AFFINITY:0=),)
OPTIONS_OBJS += src/cpuset.o
OPTIONS_OBJS += src/cpu_topo.o
endif
# OpenSSL is packaged in various forms and with various dependencies.
@ -627,7 +630,10 @@ ifneq ($(USE_OPENSSL:0=),)
SSL_LDFLAGS := $(if $(SSL_LIB),-L$(SSL_LIB)) -lssl -lcrypto
endif
USE_SSL := $(if $(USE_SSL:0=),$(USE_SSL:0=),implicit)
OPTIONS_OBJS += src/ssl_sock.o src/ssl_ckch.o src/ssl_ocsp.o src/ssl_crtlist.o src/ssl_sample.o src/cfgparse-ssl.o src/ssl_gencert.o src/ssl_utils.o src/jwt.o src/ssl_clienthello.o
OPTIONS_OBJS += src/ssl_sock.o src/ssl_ckch.o src/ssl_ocsp.o src/ssl_crtlist.o \
src/ssl_sample.o src/cfgparse-ssl.o src/ssl_gencert.o \
src/ssl_utils.o src/jwt.o src/ssl_clienthello.o src/jws.o src/acme.o \
src/ssl_trace.o
endif
ifneq ($(USE_ENGINE:0=),)
@ -654,7 +660,7 @@ OPTIONS_OBJS += src/mux_quic.o src/h3.o src/quic_rx.o src/quic_tx.o \
src/quic_cc_nocc.o src/quic_cc.o src/quic_pacing.o \
src/h3_stats.o src/quic_stats.o src/qpack-enc.o \
src/qpack-tbl.o src/quic_cc_drs.o src/quic_fctl.o \
src/cbuf.o
src/quic_enc.o
endif
ifneq ($(USE_QUIC_OPENSSL_COMPAT:0=),)
@ -978,7 +984,7 @@ OBJS += src/mux_h2.o src/mux_h1.o src/mux_fcgi.o src/log.o \
src/lb_fas.o src/clock.o src/sock_inet.o src/ev_select.o \
src/lb_map.o src/shctx.o src/mworker-prog.o src/hpack-dec.o \
src/arg.o src/signal.o src/fix.o src/dynbuf.o src/guid.o \
src/cfgparse-tcp.o src/lb_ss.o src/chunk.o \
src/cfgparse-tcp.o src/lb_ss.o src/chunk.o src/counters.o \
src/cfgparse-unix.o src/regex.o src/fcgi.o src/uri_auth.o \
src/eb64tree.o src/eb32tree.o src/eb32sctree.o src/lru.o \
src/limits.o src/ebimtree.o src/wdt.o src/hpack-tbl.o \
@ -1021,7 +1027,7 @@ help:
# TARGET variable is not set since we're not building, by definition.
IGNORE_OPTS=help install install-man install-doc install-bin \
uninstall clean tags cscope tar git-tar version update-version \
opts reg-tests reg-tests-help admin/halog/halog dev/flags/flags \
opts reg-tests reg-tests-help unit-tests admin/halog/halog dev/flags/flags \
dev/haring/haring dev/ncpu/ncpu dev/poll/poll dev/tcploop/tcploop \
dev/term_events/term_events
@ -1265,6 +1271,11 @@ reg-tests-help:
.PHONY: reg-tests reg-tests-help
unit-tests:
$(Q)$(UNIT_TEST_SCRIPT)
.PHONY: unit-tests
# "make range" iteratively builds using "make all" and the exact same build
# options for all commits within RANGE. RANGE may be either a git range
# such as ref1..ref2 or a single commit, in which case all commits from

View File

@ -1,2 +1,2 @@
$Format:%ci$
2025/02/08
2025/06/11

View File

@ -1 +1 @@
3.2-dev5
3.3-dev1

View File

@ -212,7 +212,7 @@ da_status_t da_atlas_compile(void *ctx, da_read_fn readfn, da_setpos_fn setposfn
* da_getpropid on the atlas, and if generated by the search, the ID will be consistent across
* different calls to search.
* Properties added by a search that are neither in the compiled atlas, nor in the extra_props list
* Are assigned an ID within the context that is not transferrable through different search results
* Are assigned an ID within the context that is not transferable through different search results
* within the same atlas.
* @param atlas Atlas instance
* @param extra_props properties

View File

@ -94,7 +94,7 @@ name must be preceded by a minus character ('-'). Here are examples:
* Add section description as label for all metrics
It is possible to set a description in global and proxy sections, via the
"description" directive. The global descrption is exposed if it is define via
"description" directive. The global description is exposed if it is define via
the "haproxy_process_description" metric. But the descriptions provided in proxy
sections are not dumped. However, it is possible to add it as a label for all
metrics of the corresponding section, including the global one. To do so,
@ -389,6 +389,9 @@ listed below. Metrics from extra counters are not listed.
| haproxy_server_max_connect_time_seconds |
| haproxy_server_max_response_time_seconds |
| haproxy_server_max_total_time_seconds |
| haproxy_server_agent_status |
| haproxy_server_agent_code |
| haproxy_server_agent_duration_seconds |
| haproxy_server_internal_errors_total |
| haproxy_server_unsafe_idle_connections_current |
| haproxy_server_safe_idle_connections_current |

View File

@ -32,11 +32,11 @@
/* Prometheus exporter flags (ctx->flags) */
#define PROMEX_FL_METRIC_HDR 0x00000001
#define PROMEX_FL_INFO_METRIC 0x00000002
#define PROMEX_FL_FRONT_METRIC 0x00000004
#define PROMEX_FL_BACK_METRIC 0x00000008
#define PROMEX_FL_SRV_METRIC 0x00000010
#define PROMEX_FL_LI_METRIC 0x00000020
/* unused: 0x00000002 */
/* unused: 0x00000004 */
/* unused: 0x00000008 */
/* unused: 0x00000010 */
/* unused: 0x00000020 */
#define PROMEX_FL_MODULE_METRIC 0x00000040
#define PROMEX_FL_SCOPE_GLOBAL 0x00000080
#define PROMEX_FL_SCOPE_FRONT 0x00000100

View File

@ -97,187 +97,53 @@ struct promex_ctx {
*/
#define PROMEX_MAX_METRIC_LENGTH 512
/* Global metrics */
const struct promex_metric promex_global_metrics[ST_I_INF_MAX] = {
//[ST_I_INF_NAME] ignored
//[ST_I_INF_VERSION], ignored
//[ST_I_INF_RELEASE_DATE] ignored
[ST_I_INF_NBTHREAD] = { .n = IST("nbthread"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_NBPROC] = { .n = IST("nbproc"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_PROCESS_NUM] = { .n = IST("relative_process_id"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
//[ST_I_INF_PID] ignored
//[ST_I_INF_UPTIME] ignored
[ST_I_INF_UPTIME_SEC] = { .n = IST("uptime_seconds"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_START_TIME_SEC] = { .n = IST("start_time_seconds"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
//[ST_I_INF_MEMMAX_MB] ignored
[ST_I_INF_MEMMAX_BYTES] = { .n = IST("max_memory_bytes"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
//[ST_I_INF_POOL_ALLOC_MB] ignored
[ST_I_INF_POOL_ALLOC_BYTES] = { .n = IST("pool_allocated_bytes"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
//[ST_I_INF_POOL_USED_MB] ignored
[ST_I_INF_POOL_USED_BYTES] = { .n = IST("pool_used_bytes"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_POOL_FAILED] = { .n = IST("pool_failures_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_ULIMIT_N] = { .n = IST("max_fds"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_MAXSOCK] = { .n = IST("max_sockets"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_MAXCONN] = { .n = IST("max_connections"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_HARD_MAXCONN] = { .n = IST("hard_max_connections"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_CURR_CONN] = { .n = IST("current_connections"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_CUM_CONN] = { .n = IST("connections_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_CUM_REQ] = { .n = IST("requests_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_MAX_SSL_CONNS] = { .n = IST("max_ssl_connections"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_CURR_SSL_CONNS] = { .n = IST("current_ssl_connections"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_CUM_SSL_CONNS] = { .n = IST("ssl_connections_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_MAXPIPES] = { .n = IST("max_pipes"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_PIPES_USED] = { .n = IST("pipes_used_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_PIPES_FREE] = { .n = IST("pipes_free_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_CONN_RATE] = { .n = IST("current_connection_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_CONN_RATE_LIMIT] = { .n = IST("limit_connection_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_MAX_CONN_RATE] = { .n = IST("max_connection_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_SESS_RATE] = { .n = IST("current_session_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_SESS_RATE_LIMIT] = { .n = IST("limit_session_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_MAX_SESS_RATE] = { .n = IST("max_session_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_SSL_RATE] = { .n = IST("current_ssl_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_SSL_RATE_LIMIT] = { .n = IST("limit_ssl_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_MAX_SSL_RATE] = { .n = IST("max_ssl_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_SSL_FRONTEND_KEY_RATE] = { .n = IST("current_frontend_ssl_key_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_SSL_FRONTEND_MAX_KEY_RATE] = { .n = IST("max_frontend_ssl_key_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_SSL_FRONTEND_SESSION_REUSE_PCT] = { .n = IST("frontend_ssl_reuse"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_SSL_BACKEND_KEY_RATE] = { .n = IST("current_backend_ssl_key_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_SSL_BACKEND_MAX_KEY_RATE] = { .n = IST("max_backend_ssl_key_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_SSL_CACHE_LOOKUPS] = { .n = IST("ssl_cache_lookups_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_SSL_CACHE_MISSES] = { .n = IST("ssl_cache_misses_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_COMPRESS_BPS_IN] = { .n = IST("http_comp_bytes_in_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_COMPRESS_BPS_OUT] = { .n = IST("http_comp_bytes_out_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_COMPRESS_BPS_RATE_LIM] = { .n = IST("limit_http_comp"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_ZLIB_MEM_USAGE] = { .n = IST("current_zlib_memory"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_MAX_ZLIB_MEM_USAGE] = { .n = IST("max_zlib_memory"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_TASKS] = { .n = IST("current_tasks"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_RUN_QUEUE] = { .n = IST("current_run_queue"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_IDLE_PCT] = { .n = IST("idle_time_percent"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_NODE] = { .n = IST("node"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_DESCRIPTION] = { .n = IST("description"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_STOPPING] = { .n = IST("stopping"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_JOBS] = { .n = IST("jobs"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_UNSTOPPABLE_JOBS] = { .n = IST("unstoppable_jobs"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_LISTENERS] = { .n = IST("listeners"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_ACTIVE_PEERS] = { .n = IST("active_peers"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_CONNECTED_PEERS] = { .n = IST("connected_peers"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_DROPPED_LOGS] = { .n = IST("dropped_logs_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_BUSY_POLLING] = { .n = IST("busy_polling_enabled"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_FAILED_RESOLUTIONS] = { .n = IST("failed_resolutions"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_TOTAL_BYTES_OUT] = { .n = IST("bytes_out_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_TOTAL_SPLICED_BYTES_OUT] = { .n = IST("spliced_bytes_out_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_BYTES_OUT_RATE] = { .n = IST("bytes_out_rate"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
//[ST_I_INF_DEBUG_COMMANDS_ISSUED] ignored
[ST_I_INF_CUM_LOG_MSGS] = { .n = IST("recv_logs_total"), .type = PROMEX_MT_COUNTER, .flags = PROMEX_FL_INFO_METRIC },
[ST_I_INF_BUILD_INFO] = { .n = IST("build_info"), .type = PROMEX_MT_GAUGE, .flags = PROMEX_FL_INFO_METRIC },
};
static inline enum promex_mt_type promex_global_gettype(int index, enum field_nature nature)
{
enum promex_mt_type type;
/* frontend/backend/server fields */
const struct promex_metric promex_st_metrics[ST_I_PX_MAX] = {
//[ST_I_PX_PXNAME] ignored
//[ST_I_PX_SVNAME] ignored
[ST_I_PX_QCUR] = { .n = IST("current_queue"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_QMAX] = { .n = IST("max_queue"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_SCUR] = { .n = IST("current_sessions"), .type = PROMEX_MT_GAUGE, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_SMAX] = { .n = IST("max_sessions"), .type = PROMEX_MT_GAUGE, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_SLIM] = { .n = IST("limit_sessions"), .type = PROMEX_MT_GAUGE, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_STOT] = { .n = IST("sessions_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_BIN] = { .n = IST("bytes_in_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_BOUT] = { .n = IST("bytes_out_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_DREQ] = { .n = IST("requests_denied_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC ) },
[ST_I_PX_DRESP] = { .n = IST("responses_denied_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_EREQ] = { .n = IST("request_errors_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC ) },
[ST_I_PX_ECON] = { .n = IST("connection_errors_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_ERESP] = { .n = IST("response_errors_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_WRETR] = { .n = IST("retry_warnings_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_WREDIS] = { .n = IST("redispatch_warnings_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_STATUS] = { .n = IST("status"), .type = PROMEX_MT_GAUGE, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_WEIGHT] = { .n = IST("weight"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_ACT] = { .n = IST("active_servers"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_BCK] = { .n = IST("backup_servers"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_CHKFAIL] = { .n = IST("check_failures_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_SRV_METRIC) },
[ST_I_PX_CHKDOWN] = { .n = IST("check_up_down_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_LASTCHG] = { .n = IST("check_last_change_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_DOWNTIME] = { .n = IST("downtime_seconds_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_QLIMIT] = { .n = IST("queue_limit"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
//[ST_I_PX_PID] ignored
//[ST_I_PX_IID] ignored
//[ST_I_PX_SID] ignored
[ST_I_PX_THROTTLE] = { .n = IST("current_throttle"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
[ST_I_PX_LBTOT] = { .n = IST("loadbalanced_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
//[ST_I_PX_TRACKED] ignored
//[ST_I_PX_TYPE] ignored
//[ST_I_PX_RATE] ignored
[ST_I_PX_RATE_LIM] = { .n = IST("limit_session_rate"), .type = PROMEX_MT_GAUGE, .flags = (PROMEX_FL_FRONT_METRIC ) },
[ST_I_PX_RATE_MAX] = { .n = IST("max_session_rate"), .type = PROMEX_MT_GAUGE, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_CHECK_STATUS] = { .n = IST("check_status"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
[ST_I_PX_CHECK_CODE] = { .n = IST("check_code"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
[ST_I_PX_CHECK_DURATION] = { .n = IST("check_duration_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
[ST_I_PX_HRSP_1XX] = { .n = IST("http_responses_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_HRSP_2XX] = { .n = IST("http_responses_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_HRSP_3XX] = { .n = IST("http_responses_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_HRSP_4XX] = { .n = IST("http_responses_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_HRSP_5XX] = { .n = IST("http_responses_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_HRSP_OTHER] = { .n = IST("http_responses_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
//[ST_I_PX_HANAFAIL] ignored
//[ST_I_PX_REQ_RATE] ignored
[ST_I_PX_REQ_RATE_MAX] = { .n = IST("http_requests_rate_max"), .type = PROMEX_MT_GAUGE, .flags = (PROMEX_FL_FRONT_METRIC ) },
[ST_I_PX_REQ_TOT] = { .n = IST("http_requests_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC ) },
[ST_I_PX_CLI_ABRT] = { .n = IST("client_aborts_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_SRV_ABRT] = { .n = IST("server_aborts_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_COMP_IN] = { .n = IST("http_comp_bytes_in_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC ) },
[ST_I_PX_COMP_OUT] = { .n = IST("http_comp_bytes_out_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC ) },
[ST_I_PX_COMP_BYP] = { .n = IST("http_comp_bytes_bypassed_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC ) },
[ST_I_PX_COMP_RSP] = { .n = IST("http_comp_responses_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC ) },
[ST_I_PX_LASTSESS] = { .n = IST("last_session_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
//[ST_I_PX_LAST_CHK] ignored
//[ST_I_PX_LAST_AGT] ignored
[ST_I_PX_QTIME] = { .n = IST("queue_time_average_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_CTIME] = { .n = IST("connect_time_average_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_RTIME] = { .n = IST("response_time_average_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_TTIME] = { .n = IST("total_time_average_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
//[ST_I_PX_AGENT_STATUS] ignored
//[ST_I_PX_AGENT_CODE] ignored
//[ST_I_PX_AGENT_DURATION] ignored
//[ST_I_PX_CHECK_DESC] ignored
//[ST_I_PX_AGENT_DESC] ignored
//[ST_I_PX_CHECK_RISE] ignored
//[ST_I_PX_CHECK_FALL] ignored
//[ST_I_PX_CHECK_HEALTH] ignored
//[ST_I_PX_AGENT_RISE] ignored
//[ST_I_PX_AGENT_FALL] ignored
//[ST_I_PX_AGENT_HEALTH] ignored
//[ST_I_PX_ADDR] ignored
//[ST_I_PX_COOKIE] ignored
//[ST_I_PX_MODE] ignored
//[ST_I_PX_ALGO] ignored
//[ST_I_PX_CONN_RATE] ignored
[ST_I_PX_CONN_RATE_MAX] = { .n = IST("connections_rate_max"), .type = PROMEX_MT_GAUGE, .flags = (PROMEX_FL_FRONT_METRIC ) },
[ST_I_PX_CONN_TOT] = { .n = IST("connections_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC ) },
[ST_I_PX_INTERCEPTED] = { .n = IST("intercepted_requests_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC ) },
[ST_I_PX_DCON] = { .n = IST("denied_connections_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC ) },
[ST_I_PX_DSES] = { .n = IST("denied_sessions_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC ) },
[ST_I_PX_WREW] = { .n = IST("failed_header_rewriting_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_CONNECT] = { .n = IST("connection_attempts_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_REUSE] = { .n = IST("connection_reuses_total"), .type = PROMEX_MT_COUNTER, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_CACHE_LOOKUPS] = { .n = IST("http_cache_lookups_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC ) },
[ST_I_PX_CACHE_HITS] = { .n = IST("http_cache_hits_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_BACK_METRIC ) },
[ST_I_PX_SRV_ICUR] = { .n = IST("idle_connections_current"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
[ST_I_PX_SRV_ILIM] = { .n = IST("idle_connections_limit"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
[ST_I_PX_QT_MAX] = { .n = IST("max_queue_time_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_CT_MAX] = { .n = IST("max_connect_time_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_RT_MAX] = { .n = IST("max_response_time_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_TT_MAX] = { .n = IST("max_total_time_seconds"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_EINT] = { .n = IST("internal_errors_total"), .type = PROMEX_MT_COUNTER, .flags = (PROMEX_FL_FRONT_METRIC | PROMEX_FL_LI_METRIC | PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_IDLE_CONN_CUR] = { .n = IST("unsafe_idle_connections_current"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
[ST_I_PX_SAFE_CONN_CUR] = { .n = IST("safe_idle_connections_current"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
[ST_I_PX_USED_CONN_CUR] = { .n = IST("used_connections_current"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
[ST_I_PX_NEED_CONN_EST] = { .n = IST("need_connections_current"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_SRV_METRIC) },
[ST_I_PX_UWEIGHT] = { .n = IST("uweight"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC | PROMEX_FL_SRV_METRIC) },
[ST_I_PX_AGG_SRV_CHECK_STATUS] = { .n = IST("agg_server_check_status"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC ) },
[ST_I_PX_AGG_SRV_STATUS ] = { .n = IST("agg_server_status"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC ) },
[ST_I_PX_AGG_CHECK_STATUS] = { .n = IST("agg_check_status"), .type = PROMEX_MT_GAUGE, .flags = ( PROMEX_FL_BACK_METRIC ) },
};
/* general rule that fits most types
*/
type = (nature == FN_COUNTER) ? PROMEX_MT_COUNTER : PROMEX_MT_GAUGE;
/* historically we used to consider some metrics as counters while haproxy
* doesn't consider them as such
* FIXME: maybe this is no longer needed
*/
switch (index) {
case ST_I_INF_POOL_FAILED:
case ST_I_INF_CUM_CONN:
case ST_I_INF_CUM_REQ:
case ST_I_INF_CUM_SSL_CONNS:
case ST_I_INF_PIPES_USED:
case ST_I_INF_PIPES_FREE:
case ST_I_INF_SSL_CACHE_LOOKUPS:
case ST_I_INF_SSL_CACHE_MISSES:
case ST_I_INF_COMPRESS_BPS_IN:
case ST_I_INF_COMPRESS_BPS_OUT:
case ST_I_INF_DROPPED_LOGS:
case ST_I_INF_FAILED_RESOLUTIONS:
case ST_I_INF_TOTAL_BYTES_OUT:
case ST_I_INF_TOTAL_SPLICED_BYTES_OUT:
case ST_I_INF_CUM_LOG_MSGS:
type = PROMEX_MT_COUNTER;
break;
default:
break;
}
return type;
}
static inline enum promex_mt_type promex_st_gettype(int index, enum field_nature nature)
{
enum promex_mt_type type;
/* general rule that fits most types
*/
type = (nature == FN_COUNTER) ? PROMEX_MT_COUNTER : PROMEX_MT_GAUGE;
return type;
}
/* Specialized frontend metric names, to override default ones */
const struct ist promex_st_front_metrics_names[ST_I_PX_MAX] = {
@ -307,6 +173,8 @@ const struct ist promex_st_metric_desc[ST_I_PX_MAX] = {
[ST_I_PX_CTIME] = IST("Avg. connect time for last 1024 successful connections."),
[ST_I_PX_RTIME] = IST("Avg. response time for last 1024 successful connections."),
[ST_I_PX_TTIME] = IST("Avg. total time for last 1024 successful connections."),
[ST_I_PX_AGENT_STATUS] = IST("Status of last agent check, per state label value."),
[ST_I_PX_AGENT_DURATION] = IST("Total duration of the latest server agent check, in seconds."),
[ST_I_PX_QT_MAX] = IST("Maximum observed time spent in the queue"),
[ST_I_PX_CT_MAX] = IST("Maximum observed time spent waiting for a connection to complete"),
[ST_I_PX_RT_MAX] = IST("Maximum observed time spent waiting for a server response"),
@ -569,17 +437,21 @@ static int promex_dump_global_metrics(struct appctx *appctx, struct htx *htx)
for (; ctx->field_num < ST_I_INF_MAX; ctx->field_num++) {
struct promex_label labels[PROMEX_MAX_LABELS-1] = {};
enum promex_mt_type type;
int lb_idx = 0;
if (!(promex_global_metrics[ctx->field_num].flags & ctx->flags))
if (!stat_cols_info[ctx->field_num].alt_name)
continue;
name = promex_global_metrics[ctx->field_num].n;
name = ist(stat_cols_info[ctx->field_num].alt_name);
desc = ist(stat_cols_info[ctx->field_num].desc);
if (promex_filter_metric(appctx, prefix, name))
continue;
val = stat_line_info[ctx->field_num];
type = promex_global_gettype(ctx->field_num, (val.type & FN_MASK));
switch (ctx->field_num) {
case ST_I_INF_NODE:
labels[lb_idx].name = ist("node");
@ -602,7 +474,7 @@ static int promex_dump_global_metrics(struct appctx *appctx, struct htx *htx)
break;
default:
val = stat_line_info[ctx->field_num];
break;
}
if (global.desc && ((ctx->field_num == ST_I_INF_DESCRIPTION) || (ctx->flags & PROMEX_FL_DESC_LABELS))) {
@ -612,7 +484,7 @@ static int promex_dump_global_metrics(struct appctx *appctx, struct htx *htx)
}
if (!promex_dump_ts(appctx, prefix, name, desc,
promex_global_metrics[ctx->field_num].type,
type,
&val, labels, &out, max))
goto full;
@ -648,14 +520,15 @@ static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx)
enum promex_front_state state;
for (;ctx->field_num < ST_I_PX_MAX; ctx->field_num++) {
if (!(promex_st_metrics[ctx->field_num].flags & ctx->flags))
if (!stat_cols_px[ctx->field_num].alt_name ||
!(stat_cols_px[ctx->field_num].cap & STATS_PX_CAP_FE))
continue;
name = promex_st_front_metrics_names[ctx->field_num];
desc = promex_st_metric_desc[ctx->field_num];
if (!isttest(name))
name = promex_st_metrics[ctx->field_num].n;
name = ist(stat_cols_px[ctx->field_num].alt_name);
if (!isttest(desc))
desc = ist(stat_cols_px[ctx->field_num].desc);
@ -667,6 +540,7 @@ static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx)
while (px) {
struct promex_label labels[PROMEX_MAX_LABELS-1] = {};
enum promex_mt_type type;
int lb_idx = 0;
labels[lb_idx].name = ist("proxy");
@ -686,6 +560,9 @@ static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx)
if (!stats_fill_fe_line(px, 0, stats, ST_I_PX_MAX, &(ctx->field_num)))
return -1;
val = stats[ctx->field_num];
type = promex_st_gettype(ctx->field_num, (val.type & FN_MASK));
switch (ctx->field_num) {
case ST_I_PX_STATUS:
state = !(px->flags & PR_FL_STOPPED);
@ -695,7 +572,7 @@ static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx)
val = mkf_u32(FO_STATUS, state == ctx->obj_state);
if (!promex_dump_ts(appctx, prefix, name, desc,
promex_st_metrics[ctx->field_num].type,
type,
&val, labels, &out, max))
goto full;
}
@ -712,7 +589,6 @@ static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx)
case ST_I_PX_COMP_RSP:
if (px->mode != PR_MODE_HTTP)
goto next_px;
val = stats[ctx->field_num];
break;
case ST_I_PX_HRSP_1XX:
case ST_I_PX_HRSP_2XX:
@ -726,15 +602,14 @@ static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx)
ctx->flags &= ~PROMEX_FL_METRIC_HDR;
labels[lb_idx].name = ist("code");
labels[lb_idx].value = promex_hrsp_code[ctx->field_num - ST_I_PX_HRSP_1XX];
val = stats[ctx->field_num];
break;
default:
val = stats[ctx->field_num];
break;
}
if (!promex_dump_ts(appctx, prefix, name, desc,
promex_st_metrics[ctx->field_num].type,
type,
&val, labels, &out, max))
goto full;
next_px:
@ -849,14 +724,15 @@ static int promex_dump_listener_metrics(struct appctx *appctx, struct htx *htx)
enum li_status status;
for (;ctx->field_num < ST_I_PX_MAX; ctx->field_num++) {
if (!(promex_st_metrics[ctx->field_num].flags & ctx->flags))
if (!stat_cols_px[ctx->field_num].alt_name ||
!(stat_cols_px[ctx->field_num].cap & STATS_PX_CAP_LI))
continue;
name = promex_st_li_metrics_names[ctx->field_num];
desc = promex_st_metric_desc[ctx->field_num];
if (!isttest(name))
name = promex_st_metrics[ctx->field_num].n;
name = ist(stat_cols_px[ctx->field_num].alt_name);
if (!isttest(desc))
desc = ist(stat_cols_px[ctx->field_num].desc);
@ -889,6 +765,8 @@ static int promex_dump_listener_metrics(struct appctx *appctx, struct htx *htx)
li = LIST_NEXT(&px->conf.listeners, struct listener *, by_fe);
list_for_each_entry_from(li, &px->conf.listeners, by_fe) {
enum promex_mt_type type;
if (!li->counters)
continue;
@ -899,6 +777,9 @@ static int promex_dump_listener_metrics(struct appctx *appctx, struct htx *htx)
ST_I_PX_MAX, &(ctx->field_num)))
return -1;
val = stats[ctx->field_num];
type = promex_st_gettype(ctx->field_num, (val.type & FN_MASK));
switch (ctx->field_num) {
case ST_I_PX_STATUS:
status = get_li_status(li);
@ -907,18 +788,18 @@ static int promex_dump_listener_metrics(struct appctx *appctx, struct htx *htx)
labels[lb_idx+1].name = ist("state");
labels[lb_idx+1].value = ist(li_status_st[ctx->obj_state]);
if (!promex_dump_ts(appctx, prefix, name, desc,
promex_st_metrics[ctx->field_num].type,
type,
&val, labels, &out, max))
goto full;
}
ctx->obj_state = 0;
continue;
default:
val = stats[ctx->field_num];
break;
}
if (!promex_dump_ts(appctx, prefix, name, desc,
promex_st_metrics[ctx->field_num].type,
type,
&val, labels, &out, max))
goto full;
}
@ -1051,14 +932,15 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
enum healthcheck_status srv_check_status;
for (;ctx->field_num < ST_I_PX_MAX; ctx->field_num++) {
if (!(promex_st_metrics[ctx->field_num].flags & ctx->flags))
if (!stat_cols_px[ctx->field_num].alt_name ||
!(stat_cols_px[ctx->field_num].cap & STATS_PX_CAP_BE))
continue;
name = promex_st_back_metrics_names[ctx->field_num];
desc = promex_st_metric_desc[ctx->field_num];
if (!isttest(name))
name = promex_st_metrics[ctx->field_num].n;
name = ist(stat_cols_px[ctx->field_num].alt_name);
if (!isttest(desc))
desc = ist(stat_cols_px[ctx->field_num].desc);
@ -1072,6 +954,7 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
struct promex_label labels[PROMEX_MAX_LABELS-1] = {};
unsigned int srv_state_count[PROMEX_SRV_STATE_COUNT] = { 0 };
unsigned int srv_check_count[HCHK_STATUS_SIZE] = { 0 };
enum promex_mt_type type;
const char *check_state;
int lb_idx = 0;
@ -1093,6 +976,9 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
if (!stats_fill_be_line(px, 0, stats, ST_I_PX_MAX, &(ctx->field_num)))
return -1;
val = stats[ctx->field_num];
type = promex_st_gettype(ctx->field_num, (val.type & FN_MASK));
switch (ctx->field_num) {
case ST_I_PX_AGG_SRV_CHECK_STATUS: // DEPRECATED
case ST_I_PX_AGG_SRV_STATUS:
@ -1109,7 +995,7 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
labels[lb_idx].name = ist("state");
labels[lb_idx].value = promex_srv_st[ctx->obj_state];
if (!promex_dump_ts(appctx, prefix, name, desc,
promex_st_metrics[ctx->field_num].type,
type,
&val, labels, &out, max))
goto full;
}
@ -1134,7 +1020,7 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
labels[lb_idx].name = ist("state");
labels[lb_idx].value = ist(check_state);
if (!promex_dump_ts(appctx, prefix, name, desc,
promex_st_metrics[ctx->field_num].type,
type,
&val, labels, &out, max))
goto full;
}
@ -1147,7 +1033,7 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
labels[lb_idx].value = promex_back_st[ctx->obj_state];
val = mkf_u32(FO_STATUS, bkd_state == ctx->obj_state);
if (!promex_dump_ts(appctx, prefix, name, desc,
promex_st_metrics[ctx->field_num].type,
type,
&val, labels, &out, max))
goto full;
}
@ -1194,7 +1080,6 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
case ST_I_PX_COMP_RSP:
if (px->mode != PR_MODE_HTTP)
goto next_px;
val = stats[ctx->field_num];
break;
case ST_I_PX_HRSP_1XX:
case ST_I_PX_HRSP_2XX:
@ -1208,15 +1093,14 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
ctx->flags &= ~PROMEX_FL_METRIC_HDR;
labels[lb_idx].name = ist("code");
labels[lb_idx].value = promex_hrsp_code[ctx->field_num - ST_I_PX_HRSP_1XX];
val = stats[ctx->field_num];
break;
default:
val = stats[ctx->field_num];
break;
}
if (!promex_dump_ts(appctx, prefix, name, desc,
promex_st_metrics[ctx->field_num].type,
type,
&val, labels, &out, max))
goto full;
next_px:
@ -1332,14 +1216,15 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
const char *check_state;
for (;ctx->field_num < ST_I_PX_MAX; ctx->field_num++) {
if (!(promex_st_metrics[ctx->field_num].flags & ctx->flags))
if (!stat_cols_px[ctx->field_num].alt_name ||
!(stat_cols_px[ctx->field_num].cap & STATS_PX_CAP_SRV))
continue;
name = promex_st_srv_metrics_names[ctx->field_num];
desc = promex_st_metric_desc[ctx->field_num];
if (!isttest(name))
name = promex_st_metrics[ctx->field_num].n;
name = ist(stat_cols_px[ctx->field_num].alt_name);
if (!isttest(desc))
desc = ist(stat_cols_px[ctx->field_num].desc);
@ -1351,6 +1236,7 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
while (px) {
struct promex_label labels[PROMEX_MAX_LABELS-1] = {};
enum promex_mt_type type;
int lb_idx = 0;
labels[lb_idx].name = ist("proxy");
@ -1380,6 +1266,9 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
if ((ctx->flags & PROMEX_FL_NO_MAINT_SRV) && (sv->cur_admin & SRV_ADMF_MAINT))
goto next_sv;
val = stats[ctx->field_num];
type = promex_st_gettype(ctx->field_num, (val.type & FN_MASK));
switch (ctx->field_num) {
case ST_I_PX_STATUS:
state = promex_srv_status(sv);
@ -1388,7 +1277,7 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
labels[lb_idx+1].name = ist("state");
labels[lb_idx+1].value = promex_srv_st[ctx->obj_state];
if (!promex_dump_ts(appctx, prefix, name, desc,
promex_st_metrics[ctx->field_num].type,
type,
&val, labels, &out, max))
goto full;
}
@ -1438,7 +1327,7 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
labels[lb_idx+1].name = ist("state");
labels[lb_idx+1].value = ist(check_state);
if (!promex_dump_ts(appctx, prefix, name, desc,
promex_st_metrics[ctx->field_num].type,
type,
&val, labels, &out, max))
goto full;
}
@ -1455,12 +1344,12 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
secs = (double)sv->check.duration / 1000.0;
val = mkf_flt(FN_DURATION, secs);
break;
case ST_I_PX_REQ_TOT:
if (px->mode != PR_MODE_HTTP) {
sv = NULL;
goto next_px;
}
val = stats[ctx->field_num];
break;
case ST_I_PX_HRSP_1XX:
case ST_I_PX_HRSP_2XX:
@ -1476,15 +1365,44 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
ctx->flags &= ~PROMEX_FL_METRIC_HDR;
labels[lb_idx+1].name = ist("code");
labels[lb_idx+1].value = promex_hrsp_code[ctx->field_num - ST_I_PX_HRSP_1XX];
val = stats[ctx->field_num];
break;
case ST_I_PX_AGENT_STATUS:
if ((sv->agent.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) != CHK_ST_ENABLED)
goto next_sv;
for (; ctx->obj_state < HCHK_STATUS_SIZE; ctx->obj_state++) {
if (get_check_status_result(ctx->obj_state) < CHK_RES_FAILED)
continue;
val = mkf_u32(FO_STATUS, sv->agent.status == ctx->obj_state);
check_state = get_check_status_info(ctx->obj_state);
labels[lb_idx+1].name = ist("state");
labels[lb_idx+1].value = ist(check_state);
if (!promex_dump_ts(appctx, prefix, name, desc,
type,
&val, labels, &out, max))
goto full;
}
ctx->obj_state = 0;
goto next_sv;
case ST_I_PX_AGENT_CODE:
if ((sv->agent.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) != CHK_ST_ENABLED)
goto next_sv;
val = mkf_u32(FN_OUTPUT, (sv->agent.status < HCHK_STATUS_L57DATA) ? 0 : sv->agent.code);
break;
case ST_I_PX_AGENT_DURATION:
if (sv->agent.status < HCHK_STATUS_CHECKED)
goto next_sv;
secs = (double)sv->agent.duration / 1000.0;
val = mkf_flt(FN_DURATION, secs);
break;
default:
val = stats[ctx->field_num];
break;
}
if (!promex_dump_ts(appctx, prefix, name, desc,
promex_st_metrics[ctx->field_num].type,
type,
&val, labels, &out, max))
goto full;
next_sv:
@ -1774,7 +1692,7 @@ static int promex_dump_metrics(struct appctx *appctx, struct stconn *sc, struct
switch (appctx->st1) {
case PROMEX_DUMPER_INIT:
ctx->flags |= (PROMEX_FL_METRIC_HDR|PROMEX_FL_INFO_METRIC);
ctx->flags |= PROMEX_FL_METRIC_HDR;
ctx->obj_state = 0;
ctx->field_num = ST_I_INF_NAME;
appctx->st1 = PROMEX_DUMPER_GLOBAL;
@ -1790,8 +1708,7 @@ static int promex_dump_metrics(struct appctx *appctx, struct stconn *sc, struct
}
}
ctx->flags &= ~PROMEX_FL_INFO_METRIC;
ctx->flags |= (PROMEX_FL_METRIC_HDR|PROMEX_FL_FRONT_METRIC);
ctx->flags |= PROMEX_FL_METRIC_HDR;
ctx->obj_state = 0;
ctx->field_num = ST_I_PX_PXNAME;
ctx->mod_field_num = 0;
@ -1808,8 +1725,7 @@ static int promex_dump_metrics(struct appctx *appctx, struct stconn *sc, struct
}
}
ctx->flags &= ~PROMEX_FL_FRONT_METRIC;
ctx->flags |= (PROMEX_FL_METRIC_HDR|PROMEX_FL_LI_METRIC);
ctx->flags |= PROMEX_FL_METRIC_HDR;
ctx->obj_state = 0;
ctx->field_num = ST_I_PX_PXNAME;
ctx->mod_field_num = 0;
@ -1826,8 +1742,7 @@ static int promex_dump_metrics(struct appctx *appctx, struct stconn *sc, struct
}
}
ctx->flags &= ~PROMEX_FL_LI_METRIC;
ctx->flags |= (PROMEX_FL_METRIC_HDR|PROMEX_FL_BACK_METRIC);
ctx->flags |= PROMEX_FL_METRIC_HDR;
ctx->obj_state = 0;
ctx->field_num = ST_I_PX_PXNAME;
ctx->mod_field_num = 0;
@ -1844,8 +1759,7 @@ static int promex_dump_metrics(struct appctx *appctx, struct stconn *sc, struct
}
}
ctx->flags &= ~PROMEX_FL_BACK_METRIC;
ctx->flags |= (PROMEX_FL_METRIC_HDR|PROMEX_FL_SRV_METRIC);
ctx->flags |= PROMEX_FL_METRIC_HDR;
ctx->obj_state = 0;
ctx->field_num = ST_I_PX_PXNAME;
ctx->mod_field_num = 0;
@ -1862,7 +1776,6 @@ static int promex_dump_metrics(struct appctx *appctx, struct stconn *sc, struct
}
}
ctx->flags &= ~(PROMEX_FL_METRIC_HDR|PROMEX_FL_SRV_METRIC);
ctx->flags |= (PROMEX_FL_METRIC_HDR|PROMEX_FL_MODULE_METRIC);
ctx->field_num = 0;
ctx->mod_field_num = 0;

View File

@ -25,7 +25,7 @@ end
# returns $node filled with the first node of ebroot $arg0
define ebtree_first
# browse ebtree left until encoutering leaf
# browse ebtree left until encountering leaf
set $node = (struct eb_node *)$arg0->b[0]
while 1
_ebtree_set_tag_node $node
@ -76,7 +76,7 @@ end
# returns $node filled with the first node of ebroot $arg0
define ebsctree_first
# browse ebsctree left until encoutering leaf
# browse ebsctree left until encountering leaf
set $node = (struct eb32sc_node *)$arg0->b[0]
while 1
_ebsctree_set_tag_node $node

247
dev/h2/h2-tracer.lua Normal file
View File

@ -0,0 +1,247 @@
-- This is an HTTP/2 tracer for a TCP proxy. It will decode the frames that are
-- exchanged between the client and the server and indicate their direction,
-- types, flags and lengths. Lines are prefixed with a connection number modulo
-- 4096 that allows to sort out multiplexed exchanges. In order to use this,
-- simply load this file in the global section and use it from a TCP proxy:
--
-- global
-- lua-load "dev/h2/h2-tracer.lua"
--
-- listen h2_sniffer
-- mode tcp
-- bind :8002
-- filter lua.h2-tracer #hex
-- server s1 127.0.0.1:8003
--
-- define the decoder's class here
Dec = {}
Dec.id = "Lua H2 tracer"
Dec.flags = 0
Dec.__index = Dec
Dec.args = {} -- args passed by the filter's declaration
Dec.cid = 0 -- next connection ID
-- prefix to indent responses
res_pfx = " | "
-- H2 frame types
h2ft = {
[0] = "DATA",
[1] = "HEADERS",
[2] = "PRIORITY",
[3] = "RST_STREAM",
[4] = "SETTINGS",
[5] = "PUSH_PROMISE",
[6] = "PING",
[7] = "GOAWAY",
[8] = "WINDOW_UPDATE",
[9] = "CONTINUATION",
}
h2ff = {
[0] = { [0] = "ES", [3] = "PADDED" }, -- data
[1] = { [0] = "ES", [2] = "EH", [3] = "PADDED", [5] = "PRIORITY" }, -- headers
[2] = { }, -- priority
[3] = { }, -- rst_stream
[4] = { [0] = "ACK" }, -- settings
[5] = { [2] = "EH", [3] = "PADDED" }, -- push_promise
[6] = { [0] = "ACK" }, -- ping
[7] = { }, -- goaway
[8] = { }, -- window_update
[9] = { [2] = "EH" }, -- continuation
}
function Dec:new()
local dec = {}
setmetatable(dec, Dec)
dec.do_hex = false
if (Dec.args[1] == "hex") then
dec.do_hex = true
end
Dec.cid = Dec.cid+1
-- mix the thread number when multithreading.
dec.cid = Dec.cid + 64 * core.thread
-- state per dir. [1]=req [2]=res
dec.st = {
[1] = {
hdr = { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
fofs = 0,
flen = 0,
ftyp = 0,
fflg = 0,
sid = 0,
tot = 0,
},
[2] = {
hdr = { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
fofs = 0,
flen = 0,
ftyp = 0,
fflg = 0,
sid = 0,
tot = 0,
},
}
return dec
end
function Dec:start_analyze(txn, chn)
if chn:is_resp() then
io.write(string.format("[%03x] ", self.cid % 4096) .. res_pfx .. "### res start\n")
else
io.write(string.format("[%03x] ", self.cid % 4096) .. "### req start\n")
end
filter.register_data_filter(self, chn)
end
function Dec:end_analyze(txn, chn)
if chn:is_resp() then
io.write(string.format("[%03x] ", self.cid % 4096) .. res_pfx .. "### res end: " .. self.st[2].tot .. " bytes total\n")
else
io.write(string.format("[%03x] ", self.cid % 4096) .. "### req end: " ..self.st[1].tot.. " bytes total\n")
end
end
function Dec:tcp_payload(txn, chn)
local data = { }
local dofs = 1
local pfx = ""
local dir = 1
local sofs = 0
local ft = ""
local ff = ""
if chn:is_resp() then
pfx = res_pfx
dir = 2
end
pfx = string.format("[%03x] ", self.cid % 4096) .. pfx
-- stream offset before processing
sofs = self.st[dir].tot
if (chn:input() > 0) then
data = chn:data()
self.st[dir].tot = self.st[dir].tot + chn:input()
end
if (chn:input() > 0 and self.do_hex ~= false) then
io.write("\n" .. pfx .. "Hex:\n")
for i = 1, #data do
if ((i & 7) == 1) then io.write(pfx) end
io.write(string.format("0x%02x ", data:sub(i, i):byte()))
if ((i & 7) == 0 or i == #data) then io.write("\n") end
end
end
-- start at byte 1 in the <data> string
dofs = 1
-- the first 24 bytes are expected to be an H2 preface on the request
if (dir == 1 and sofs < 24) then
-- let's not check it for now
local bytes = self.st[dir].tot - sofs
if (sofs + self.st[dir].tot >= 24) then
-- skip what was missing from the preface
dofs = dofs + 24 - sofs
sofs = 24
io.write(pfx .. "[PREFACE len=24]\n")
else
-- consume more preface bytes
sofs = sofs + self.st[dir].tot
return
end
end
-- parse contents as long as there are pending data
while true do
-- check if we need to consume data from the current frame
-- flen is the number of bytes left before the frame's end.
if (self.st[dir].flen > 0) then
if dofs > #data then return end -- missing data
if (#data - dofs + 1 < self.st[dir].flen) then
-- insufficient data
self.st[dir].flen = self.st[dir].flen - (#data - dofs + 1)
io.write(pfx .. string.format("%32s\n", "... -" .. (#data - dofs + 1) .. " = " .. self.st[dir].flen))
dofs = #data + 1
return
else
-- enough data to finish
if (dofs == 1) then
-- only print a partial size if the frame was interrupted
io.write(pfx .. string.format("%32s\n", "... -" .. self.st[dir].flen .. " = 0"))
end
dofs = dofs + self.st[dir].flen
self.st[dir].flen = 0
end
end
-- here, flen = 0, we're at the beginning of a new frame --
-- read possibly missing header bytes until dec.fofs == 9
while self.st[dir].fofs < 9 do
if dofs > #data then return end -- missing data
self.st[dir].hdr[self.st[dir].fofs + 1] = data:sub(dofs, dofs):byte()
dofs = dofs + 1
self.st[dir].fofs = self.st[dir].fofs + 1
end
-- we have a full frame header here
if (self.do_hex ~= false) then
io.write("\n" .. pfx .. string.format("hdr=%02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
self.st[dir].hdr[1], self.st[dir].hdr[2], self.st[dir].hdr[3],
self.st[dir].hdr[4], self.st[dir].hdr[5], self.st[dir].hdr[6],
self.st[dir].hdr[7], self.st[dir].hdr[8], self.st[dir].hdr[9]))
end
-- we have a full frame header, we'll be ready
-- for a new frame once the data is gone
self.st[dir].flen = self.st[dir].hdr[1] * 65536 +
self.st[dir].hdr[2] * 256 +
self.st[dir].hdr[3]
self.st[dir].ftyp = self.st[dir].hdr[4]
self.st[dir].fflg = self.st[dir].hdr[5]
self.st[dir].sid = self.st[dir].hdr[6] * 16777216 +
self.st[dir].hdr[7] * 65536 +
self.st[dir].hdr[8] * 256 +
self.st[dir].hdr[9]
self.st[dir].fofs = 0
-- decode frame type
if self.st[dir].ftyp <= 9 then
ft = h2ft[self.st[dir].ftyp]
else
ft = string.format("TYPE_0x%02x\n", self.st[dir].ftyp)
end
-- decode frame flags for frame type <ftyp>
ff = ""
for i = 7, 0, -1 do
if (((self.st[dir].fflg >> i) & 1) ~= 0) then
if self.st[dir].ftyp <= 9 and h2ff[self.st[dir].ftyp][i] ~= nil then
ff = ff .. ((ff == "") and "" or "+")
ff = ff .. h2ff[self.st[dir].ftyp][i]
else
ff = ff .. ((ff == "") and "" or "+")
ff = ff .. string.format("0x%02x", 1<<i)
end
end
end
io.write(pfx .. string.format("[%s %ssid=%u len=%u (bytes=%u)]\n",
ft, (ff == "") and "" or ff .. " ",
self.st[dir].sid, self.st[dir].flen,
(#data - dofs + 1)))
end
end
core.register_filter("h2-tracer", Dec, function(dec, args)
Dec.args = args
return dec
end)

View File

@ -1,4 +1,5 @@
#define _GNU_SOURCE
#include <errno.h>
#include <limits.h>
#include <sched.h>
#include <stdio.h>
@ -11,6 +12,22 @@
static char prog_full_path[PATH_MAX];
long sysconf(int name)
{
if (name == _SC_NPROCESSORS_ONLN ||
name == _SC_NPROCESSORS_CONF) {
const char *ncpu = getenv("NCPU");
int n;
n = ncpu ? atoi(ncpu) : CPU_SETSIZE;
if (n < 0 || n > CPU_SETSIZE)
n = CPU_SETSIZE;
return n;
}
errno = EINVAL;
return -1;
}
/* return a cpu_set having the first $NCPU set */
int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask)
{

View File

@ -0,0 +1,70 @@
BEGININPUT
BEGINCONTEXT
HAProxy's development cycle consists in one development branch, and multiple
maintenance branches.
All the development is made into the development branch exclusively. This
includes mostly new features, doc updates, cleanups and or course, fixes.
The maintenance branches, also called stable branches, never see any
development, and only receive ultra-safe fixes for bugs that affect them,
that are picked from the development branch.
Branches are numbered in 0.1 increments. Every 6 months, upon a new major
release, the development branch enters maintenance and a new development branch
is created with a new, higher version. The current development branch is
3.3-dev, and maintenance branches are 3.2 and below.
Fixes created in the development branch for issues that were introduced in an
earlier branch are applied in descending order to each and every version till
that branch that introduced the issue: 3.2 first, then 3.1, then 3.0, then 2.9
and so on. This operation is called "backporting". A fix for an issue is never
backported beyond the branch that introduced the issue. An important point is
that the project maintainers really aim at zero regression in maintenance
branches, so they're never willing to take any risk backporting patches that
are not deemed strictly necessary.
Fixes consist of patches managed using the Git version control tool and are
identified by a Git commit ID and a commit message. For this reason we
indistinctly talk about backporting fixes, commits, or patches; all mean the
same thing. When mentioning commit IDs, developers always use a short form
made of the first 8 characters only, and expect the AI assistant to do the
same.
It seldom happens that some fixes depend on changes that were brought by other
patches that were not in some branches and that will need to be backported as
well for the fix to work. In this case, such information is explicitly provided
in the commit message by the patch's author in natural language.
Developers are serious and always indicate if a patch needs to be backported.
Sometimes they omit the exact target branch, or they will say that the patch is
"needed" in some older branch, but it means the same. If a commit message
doesn't mention any backport instructions, it means that the commit does not
have to be backported. And patches that are not strictly bug fixes nor doc
improvements are normally not backported. For example, fixes for design
limitations, architectural improvements and performance optimizations are
considered too risky for a backport. Finally, all bug fixes are tagged as
"BUG" at the beginning of their subject line. Patches that are not tagged as
such are not bugs, and must never be backported unless their commit message
explicitly requests so.
ENDCONTEXT
A developer is reviewing the development branch, trying to spot which commits
need to be backported to maintenance branches. This person is already expert
on HAProxy and everything related to Git, patch management, and the risks
associated with backports, so he doesn't want to be told how to proceed nor to
review the contents of the patch.
The goal for this developer is to get some help from the AI assistant to save
some precious time on this tedious review work. In order to do a better job, he
needs an accurate summary of the information and instructions found in each
commit message. Specifically he needs to figure if the patch fixes a problem
affecting an older branch or not, if it needs to be backported, if so to which
branches, and if other patches need to be backported along with it.
The indented text block below after an "id" line and starting with a Subject line
is a commit message from the HAProxy development branch that describes a patch
applied to that branch, starting with its subject line, please read it carefully.

View File

@ -0,0 +1,29 @@
ENDINPUT
BEGININSTRUCTION
You are an AI assistant that follows instruction extremely well. Help as much
as you can, responding to a single question using a single response.
The developer wants to know if he needs to backport the patch above to fix
maintenance branches, for which branches, and what possible dependencies might
be mentioned in the commit message. Carefully study the commit message and its
backporting instructions if any (otherwise it should probably not be backported),
then provide a very concise and short summary that will help the developer decide
to backport it, or simply to skip it.
Start by explaining in one or two sentences what you recommend for this one and why.
Finally, based on your analysis, give your general conclusion as "Conclusion: X"
where X is a single word among:
- "yes", if you recommend to backport the patch right now either because
it explicitly states this or because it's a fix for a bug that affects
a maintenance branch (3.2 or lower);
- "wait", if this patch explicitly mentions that it must be backported, but
only after waiting some time.
- "no", if nothing clearly indicates a necessity to backport this patch (e.g.
lack of explicit backport instructions, or it's just an improvement);
- "uncertain" otherwise for cases not covered above
ENDINSTRUCTION
Explanation:

View File

@ -362,7 +362,7 @@ option set-process-time <var name>
latency added by the SPOE processing for the last handled event or group.
If several events or groups are processed for the same stream, this value
will be overrideen.
will be overridden.
See also: "option set-total-time".

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -376,6 +376,9 @@ mt_list_lock_prev(elt)
Return A elt
value: <===>
mt_list_try_lock_prev(elt)
Does the same thing as mt_list_lock_prev(), except if the list is
locked already, it returns { NULL, NULL } instead of waiting.
mt_list_lock_elem(elt)
Locks the element only. Both of its pointers are replaced by two locked

View File

@ -204,6 +204,14 @@ the cache, when this option is set, objects are picked from the cache from the
oldest one instead of the freshest one. This way even late memory corruptions
have a chance to be detected.
Another non-destructive approach is to use "-dMbackup". A full copy of the
object is made after its end, which eases inspection (e.g. of the parts
scratched by the pool_item elements), and a comparison is made upon allocation
of that object, just like with "-dMintegrity", causing a crash on mismatch. The
initial 4 words corresponding to the list are ignored as well. Note that when
both "-dMbackup" and "-dMintegrity" are used, the copy is performed before
being scratched, and the comparison is done by "-dMintegrity" only.
When build option DEBUG_MEMORY_POOLS is set, or the boot-time option "-dMtag"
is passed on the executable's command line, pool objects are allocated with
one extra pointer compared to the requested size, so that the bytes that follow
@ -342,7 +350,9 @@ struct pool_head *create_pool(char *name, uint size, uint flags)
"-dMno-merge" is passed on the executable's command line, the pools
also need to have the exact same name to be merged. In addition, unless
MEM_F_EXACT is set in <flags>, the object size will usually be rounded
up to the size of pointers (16 or 32 bytes). The name that will appear
up to the size of pointers (16 or 32 bytes). MEM_F_UAF may be set on a
per-pool basis to enable the UAF detection only for this specific pool,
saving the massive overhead of global usage. The name that will appear
in the pool upon merging is the name of the first created pool. The
returned pointer is the new (or reused) pool head, or NULL upon error.
Pools created this way must be destroyed using pool_destroy().

View File

@ -21,7 +21,7 @@ falls back to CLOCK_REALTIME. The former is more accurate as it really counts
the time spent in the process, while the latter might also account for time
stuck on paging in etc.
Then wdt_ping() is called to arm the timer. t's set to trigger every
Then wdt_ping() is called to arm the timer. It's set to trigger every
<wdt_warn_blocked_traffic_ns> interval. It is also called by wdt_handler()
to reprogram a new wakeup after it has ticked.
@ -37,15 +37,18 @@ If the thread was not marked as stuck, it's verified that no progress was made
for at least one second, in which case the TH_FL_STUCK flag is set. The lack of
progress is measured by the distance between the thread's current cpu_time and
its prev_cpu_time. If the lack of progress is at least as large as the warning
threshold and no context switch happened since last call, ha_stuck_warning() is
called to emit a warning about that thread. In any case the context switch
counter for that thread is updated.
threshold, then the signal is bounced to the faulty thread if it's not the
current one. Since this bounce is based on the time spent without update, it
already doesn't happen often.
If the thread was already marked as stuck, then the thread is considered as
definitely stuck. Then ha_panic() is directly called if the thread is the
current one, otherwise ha_kill() is used to resend the signal directly to the
target thread, which will in turn go through this handler and handle the panic
itself.
Once on the faulty thread, two checks are performed:
1) if the thread was already marked as stuck, then the thread is considered
as definitely stuck, and ha_panic() is called. It will not return.
2) a check is made to verify if the scheduler is still ticking, by reading
and setting a variable that only the scheduler can clear when leaving a
task. If the scheduler didn't make any progress, ha_stuck_warning() is
called to emit a warning about that thread.
Most of the time there's no panic of course, and a wdt_ping() is performed
before leaving the handler to reprogram a check for that thread.
@ -61,12 +64,12 @@ set TAINTED_WARN_BLOCKED_TRAFFIC.
ha_panic() uses the current thread's trash buffer to produce the messages, as
we don't care about its contents since that thread will never return. However
ha_stuck_warning() instead uses a local 4kB buffer in the thread's stack.
ha_stuck_warning() instead uses a local 8kB buffer in the thread's stack.
ha_panic() will call ha_thread_dump_fill() for each thread, to complete the
buffer being filled with each thread's dump messages. ha_stuck_warning() only
calls the function for the current thread. In both cases the message is then
directly sent to fd #2 (stderr) and ha_thread_dump_one() is called to release
the dumped thread.
calls ha_thread_dump_one(), which works on the current thread. In both cases
the message is then directly sent to fd #2 (stderr) and ha_thread_dump_done()
is called to release the dumped thread.
Both print a few extra messages, but ha_panic() just ends by looping on abort()
until the process dies.
@ -85,6 +88,12 @@ to point to the target buffer. The thread_dump_buffer has 4 possible values:
will keep their own copy of their own dump so that it can be later found in
the core file for inspection.
A copy of the last valid thread_dump_buffer used is kept in last_dump_buffer,
for easier post-mortem analysis. This one may be NULL or even invalid, but
usually during a panic it will be valid, and may reveal useful hints even if it
still contains the dump of the last warning. Usually this will point to a trash
buffer or to stack area.
ha_thread_dump_fill() then either directly calls ha_thread_dump_one() if the
target thread is the current thread, or sends the target thread DEBUGSIG
(SIGURG) if it's a different thread. This signal is initialized at boot time
@ -104,13 +113,19 @@ ha_dump_backtrace() before returning.
ha_dump_backtrace() produces a backtrace into a local buffer (100 entries max),
then dumps the code bytes nearby the crashing instrution, dumps pointers and
tries to resolve function names, and sends all of that into the target buffer.
On some architectures (x86_64, arm64), it will also try to detect and decode
call instructions and resolve them to called functions.
3. Improvements
---------------
The symbols resolution is extremely expensive, particularly for the warnings
which should be fast. But we need it, it's just unfortunate that it strikes at
the wrong moment.
the wrong moment. At least ha_dump_backtrace() does disable signals while it's
resolving, in order to avoid unwanted re-entrance. In addition, the called
function resolve_sym_name() uses some locking and refrains from calling the
dladdr family of functions in a re-entrant way (in the worst case only well
known symbols will be resolved)..
In an ideal case, ha_dump_backtrace() would dump the pointers to a local array,
which would then later be resolved asynchronously in a tasklet. This can work

View File

@ -1,7 +1,7 @@
-----------------------
HAProxy Starter Guide
-----------------------
version 3.2
version 3.3
This document is an introduction to HAProxy for all those who don't know it, as

View File

@ -893,7 +893,9 @@ Core class
**context**: init, task, action
This function returns a new object of a *httpclient* class.
This function returns a new object of a *httpclient* class. An *httpclient*
object must be used to process one and only one request. It must never be
reused to process several requests.
:returns: A :ref:`httpclient_class` object.
@ -926,12 +928,25 @@ Core class
its work and wants to give back the control to HAProxy without executing the
remaining code. It can be seen as a multi-level "return".
.. js:function:: core.wait([milliseconds])
**context**: task, action
Give back the hand at the HAProxy scheduler. Unlike :js:func:`core.yield`
the task will not be woken up automatically to resume as fast as possible.
Instead, it will wait for an event to wake the task. If milliseconds argument
is provided then the Lua excecution will be automatically resumed passed this
delay even if no event caused the task to wake itself up.
:param integer milliseconds: automatic wakeup passed this delay. (optional)
.. js:function:: core.yield()
**context**: task, action
Give back the hand at the HAProxy scheduler. It is used when the LUA
processing consumes a lot of processing time.
processing consumes a lot of processing time. Lua excecution will be resumed
automatically (automatic reschedule).
.. js:function:: core.parse_addr(address)
@ -1869,6 +1884,17 @@ Queue class
Use :js:func:`core.queue` to get a new Queue object.
.. js:function:: Queue.alarm()
**context**: task, action, service
Sets a wakeup alarm on the current Lua context so that when new data
becomes available on the Queue, the current Lua context is woken up
automatically. It can be combined with :js:func:`core.wait` to wait
for Queue events.
:param class_queue queue: A :ref:`queue_class` to the current queue
.. js:function:: Queue.size(queue)
This function returns the number of items within the Queue.
@ -2557,7 +2583,9 @@ HTTPClient class
.. js:class:: HTTPClient
The httpclient class allows issue of outbound HTTP requests through a simple
API without the knowledge of HAProxy internals.
API without the knowledge of HAProxy internals. Any instance must be used to
process one and only one request. It must never be reused to process several
requests.
.. js:function:: HTTPClient.get(httpclient, request)
.. js:function:: HTTPClient.head(httpclient, request)
@ -3464,7 +3492,7 @@ Patref class
in case of duplicated entries, only the first matching entry is returned.
.. Warning::
Not meant to be shared bewteen multiple contexts. If multiple contexts
Not meant to be shared between multiple contexts. If multiple contexts
need to work on the same pattern reference, each context should have
its own patref object.
@ -3494,7 +3522,7 @@ Patref class
.. js:function:: Patref.commit(ref)
Tries to commit pending Patref object updates, that is updates made to the
local object will be committed to the underlying patter reference storage
local object will be committed to the underlying pattern reference storage
in an atomic manner upon success. Upon failure, local pending updates are
lost. Upon success, all other pending updates on the pattern reference
(e.g.: "prepare" from the cli or from other Patref Lua objects) started
@ -3886,16 +3914,31 @@ AppletTCP class
:param class_AppletTCP applet: An :ref:`applettcp_class`
:returns: a string. The string can be empty if we reach the end of the stream.
.. js:function:: AppletTCP.receive(applet, [size])
.. js:function:: AppletTCP.receive(applet, [size, [timeout]])
Reads data from the TCP stream, according to the specified read *size*. If the
*size* is missing, the function tries to read all the content of the stream
until the end.
until the end. An optional timeout may be specified in milliseconds. In this
case the function will return no longer than this delay, with the amount of
available data, or nil if there is no data. An empty string is returned if the
connection is closed.
:param class_AppletTCP applet: An :ref:`applettcp_class`
:param integer size: the required read size.
:returns: always return a string, the string can be empty if the connection is
closed.
:returns: return nil if the timeout has expired and no data was available but
can still be received. Otherwise, a string is returned, possibly an empty
string if the connection is closed.
.. js:function:: AppletTCP.try_receive(applet)
Reads available data from the TCP stream and returns immediately. Returns a
string containing read bytes or nil if no bytes are available at that time. An
empty string is returned if the connection is closed.
:param class_AppletTCP applet: An :ref:`applettcp_class`
:returns: return nil if no data was available but can still be
received. Otherwise, a string is returned, possibly an empty string if the
connection is closed.
.. js:function:: AppletTCP.send(appletmsg)
@ -4572,6 +4615,27 @@ HTTPMessage class
data by default.
:returns: an integer containing the amount of bytes copied or -1.
.. js:function:: HTTPMessage.set_body_len(http_msg, length)
This function changes the expected payload length of the HTTP message
**http_msg**. **length** can be an integer value. In that case, a
"Content-Length" header is added with the given value. It is also possible to
pass the **"chunked"** string instead of an integer value to force the HTTP
message to be chunk-encoded. In that case, a "Transfer-Encoding" header is
added with the "chunked" value. In both cases, all existing "Content-Length"
and "Transfer-Encoding" headers are removed.
This function should be used in the filter context to be able to alter the
payload of the HTTP message. The internal state of the HTTP message is updated
accordingly. :js:func:`HTTPMessage.add_header()` or
:js:func:`HTTPMessage.set_header()` functions must be used in that case.
:param class_httpmessage http_msg: The manipulated HTTP message.
:param type length: The new payload length to set. It can be an integer or
the string "chunked".
:returns: true if the payload length was successfully updated, false
otherwise.
.. js:function:: HTTPMessage.set_eom(http_msg)
This function set the end of message for the HTTP message **http_msg**.

View File

@ -1,7 +1,7 @@
------------------------
HAProxy Management Guide
------------------------
version 3.2
version 3.3
This document describes how to start, stop, manage, and troubleshoot HAProxy,
@ -192,6 +192,11 @@ list of options is :
-Ws : master-worker mode with support of `notify` type of systemd service.
-4 : force DNS resolvers to query and accept IPv4 addresses only ("A"
records). This can be used when facing difficulties in certain
environments lacking end-to-end dual-stack connectivity. It overrides
the global "dns-accept-family" directive and forces it to "ipv4".
-c : only performs a check of the configuration files and exits before trying
to bind. The exit status is zero if everything is OK, or non-zero if an
error is encountered. Presence of warnings will be reported if any.
@ -320,6 +325,16 @@ list of options is :
last released. This works best with "no-merge", "cold-first" and "tag".
Enabling this option will slightly increase the CPU usage.
- backup / no-backup:
This option performs a copy of each released object at release time,
allowing developers to inspect them. It also performs a comparison at
allocation time to detect if anything changed in between, indicating a
use-after-free condition. This doubles the memory usage and slightly
increases the CPU usage (similar to "integrity"). If combined with
"integrity", it still duplicates the contents but doesn't perform the
comparison (which is performed by "integrity"). Just like "integrity",
it works best with "no-merge", "cold-first" and "tag".
- no-global / global:
Depending on the operating system, a process-wide global memory cache
may be enabled if it is estimated that the standard allocator is too
@ -394,6 +409,9 @@ list of options is :
foreground. It is mainly used during development or during small tests, as
Ctrl-C is enough to stop the process. Never use it in an init script.
-dc : enable CPU affinity debugging. The list of selected and evicted CPUs as
well as their topology will be reported before starting.
-de : disable the use of the "epoll" poller. It is equivalent to the "global"
section's keyword "noepoll". It is mostly useful when suspecting a bug
related to this poller. On systems supporting epoll, the fallback will
@ -1122,9 +1140,9 @@ CSV output format for monitoring tools. The same format is provided on the
Unix socket.
Statistics are regroup in categories labelled as domains, corresponding to the
multiple components of HAProxy. There are two domains available: proxy and dns.
If not specified, the proxy domain is selected. Note that only the proxy
statistics are printed on the HTTP page.
multiple components of HAProxy. There are two domains available: proxy and
resolvers. If not specified, the proxy domain is selected. Note that only the
proxy statistics are printed on the HTTP page.
9.1. CSV format
---------------
@ -1503,9 +1521,10 @@ that the terminal is handled by the readline library which supports line
editing and history, which is very convenient when issuing repeated commands
(eg: watch a counter).
The socket supports two operation modes :
- interactive
- non-interactive
The socket supports three operation modes :
- non-interactive, silent
- interactive, silent
- interactive with prompt
The non-interactive mode is the default when socat connects to the socket. In
this mode, a single line may be sent. It is processed as a whole, responses are
@ -1519,12 +1538,25 @@ example :
If a command needs to use a semi-colon or a backslash (eg: in a value), it
must be preceded by a backslash ('\').
The interactive mode displays a prompt ('>') and waits for commands to be
entered on the line, then processes them, and displays the prompt again to wait
for a new command. This mode is entered via the "prompt" command which must be
sent on the first line in non-interactive mode. The mode is a flip switch, if
"prompt" is sent in interactive mode, it is disabled and the connection closes
after processing the last command of the same line.
The interactive mode allows new commands to be sent after the ones from the
previous lines finish. It exists in two variants, one silent, which works like
the non-interactive mode except that the socket waits for a new command instead
of closing, and one where a prompt is displayed ('>') at the beginning of the
line. The interactive mode is preferred for advanced tools while the prompt
mode is preferred for humans.
The mode can be changed using the "prompt" command. By default, it toggles the
interactive+prompt modes. Entering "prompt" in interactive mode will switch to
prompt mode. The command optionally takes a specific mode among which:
- "n" : non-interactive mode (single command and quits)
- "i" : interactive mode (multiple commands, no prompt)
- "p" : prompt mode (multiple commands with a prompt)
Since the default mode is non-interactive, "prompt" must be used as the first
command in order to switch it, otherwise the previous command will cause the
connection to be closed. Switching to non-interactive mode will result in the
connection to be closed after all the commands of the same line complete.
For this reason, when debugging by hand, it's quite common to start with the
"prompt" command :
@ -1535,6 +1567,9 @@ For this reason, when debugging by hand, it's quite common to start with the
...
>
Interactive tools might prefer starting with "prompt i" to switch to interactive
mode without the prompt.
Optionally the process' uptime may be displayed in the prompt. In order to
enable this, the "prompt timed" command will enable the prompt and toggle the
displaying of the time. The uptime is displayed in format "d:hh:mm:ss" where
@ -1616,6 +1651,29 @@ abort ssl crl-file <crlfile>
See also "set ssl crl-file" and "commit ssl crl-file".
acme renew <certificate>
Starts an ACME certificate generation task with the given certificate name.
The certificate must be linked to an acme section, see section 12.8 "ACME"
of the configuration manual. See also "acme status".
acme status
Show the status of every certificates that were configured with ACME.
This command outputs, separated by a tab:
- The name of the certificate configured in haproxy
- The acme section used in the configuration
- The state of the acme task, either "Running", "Scheduled" or "Stopped"
- The UTC expiration date of the certificate in ISO8601 format
- The relative expiration time (0d if expired)
- The UTC scheduled date of the certificate in ISO8601 format
- The relative schedule time (0d if Running)
Example:
$ echo "@1; acme status" | socat /tmp/master.sock - | column -t -s $'\t'
# certificate section state expiration date (UTC) expires in scheduled date (UTC) scheduled in
ecdsa.pem LE Running 2020-01-18T09:31:12Z 0d 0h00m00s 2020-01-15T21:31:12Z 0d 0h00m00s
foobar.pem.rsa LE Scheduled 2025-08-04T11:50:54Z 89d 23h01m13s 2025-07-27T23:50:55Z 82d 11h01m14s
add acl [@<ver>] <acl> <pattern>
Add an entry into the acl <acl>. <acl> is the #<id> or the <name> returned by
"show acl". This command does not verify if the entry already exists. Entries
@ -1666,8 +1724,9 @@ add server <backend>/<server> [args]*
The <server> name must not be already used in the backend. A special
restriction is put on the backend which must used a dynamic load-balancing
algorithm. A subset of keywords from the server config file statement can be
used to configure the server behavior. Also note that no settings will be
reused from an hypothetical 'default-server' statement in the same backend.
used to configure the server behavior (see "add server help" to list them).
Also note that no settings will be reused from an hypothetical
'default-server' statement in the same backend.
Currently a dynamic server is statically initialized with the "none"
init-addr method. This means that no resolution will be undertaken if a FQDN
@ -1697,78 +1756,10 @@ add server <backend>/<server> [args]*
servers. Please refer to the "u-limit" global keyword documentation in this
case.
Here is the list of the currently supported keywords :
- agent-addr
- agent-check
- agent-inter
- agent-port
- agent-send
- allow-0rtt
- alpn
- addr
- backup
- ca-file
- check
- check-alpn
- check-proto
- check-send-proxy
- check-sni
- check-ssl
- check-via-socks4
- ciphers
- ciphersuites
- cookie
- crl-file
- crt
- disabled
- downinter
- error-limit
- fall
- fastinter
- force-sslv3/tlsv10/tlsv11/tlsv12/tlsv13
- id
- init-state
- inter
- maxconn
- maxqueue
- minconn
- no-ssl-reuse
- no-sslv3/tlsv10/tlsv11/tlsv12/tlsv13
- no-tls-tickets
- npn
- observe
- on-error
- on-marked-down
- on-marked-up
- pool-low-conn
- pool-max-conn
- pool-purge-delay
- port
- proto
- proxy-v2-options
- rise
- send-proxy
- send-proxy-v2
- send-proxy-v2-ssl
- send-proxy-v2-ssl-cn
- slowstart
- sni
- source
- ssl
- ssl-max-ver
- ssl-min-ver
- tfo
- tls-tickets
- track
- usesrc
- verify
- verifyhost
- weight
- ws
Their syntax is similar to the server line from the configuration file,
please refer to their individual documentation for details.
add server help
List the keywords supported for dynamic servers by the current haproxy
version. Keyword syntax is similar to the server line from the configuration
file, please refer to their individual documentation for details.
add ssl ca-file <cafile> <payload>
Add a new certificate to a ca-file. This command is useful when you reached
@ -1991,9 +1982,9 @@ commit ssl crl-file <crlfile>
See also "new ssl crl-file", "set ssl crl-file", "abort ssl crl-file" and
"add ssl crt-list".
debug counters [reset|show|all|bug|chk|cnt|glt|?]*
debug counters [reset|show|on|off|all|bug|chk|cnt|glt|?]*
List internal counters placed in the code, which may vary depending on some
build options. Some of them depend on DEBUG_STRICT, others on DEBUG_GLITCHES.
build options. Some of them depend on DEBUG_STRICT, others on DEBUG_COUNTERS.
The command takes a combination of multiple arguments, some defining actions
and others defining filters:
- bug enables listing the counters for BUG_ON() statements
@ -2001,6 +1992,8 @@ debug counters [reset|show|all|bug|chk|cnt|glt|?]*
- chk enables listing the counters for CHECK_IF() statements
- glt enables listing the counters for COUNT_GLITCH() statements
- all enables showing counters that never triggered (value 0)
- off action: disables updating of the COUNT_IF() counters
- on action: enables updating of the COUNT_IF() counters
- reset action: resets all specified counters
- show action: shows all specified counters
@ -2354,15 +2347,27 @@ prepare map <map>
committed. Version numbers are unsigned 32-bit values which wrap at the end,
so care must be taken when comparing them in an external program.
prompt
Toggle the prompt at the beginning of the line and enter or leave interactive
mode. In interactive mode, the connection is not closed after a command
completes. Instead, the prompt will appear again, indicating the user that
the interpreter is waiting for a new command. The prompt consists in a right
angle bracket followed by a space "> ". This mode is particularly convenient
when one wants to periodically check information such as stats or errors.
It is also a good idea to enter interactive mode before issuing a "help"
command.
prompt [help | n | i | p | timed]*
Changes the behavior of the interactive mode and the prompt displayed at the
beginning of the line in interactive mode:
- "help" : displays the command's usage
- "n" : switches to non-interactive mode
- "i" : switches to interactive mode
- "p" : switches to interactive + prompt mode
- "timed" : toggles displaying the time in the prompt
Without any option, this will cycle through prompt mode then non-interactive
mode. In non-interactive mode, the connection is closed after the last
command of the current line compltes. In interactive mode, the connection is
not closed after a command completes, so that a new one can be entered. In
prompt mode, the interactive mode is still in use, and a prompt will appear
at the beginning of the line, indicating to the user that the interpreter is
waiting for a new command. The prompt consists in a right angle bracket
followed by a space "> ".
The prompt mode is more suited to human users, the interactive mode to
advanced scripts, and the non-interactive mode (default) to basic scripts.
Note that the non-interactive mode is not available for the master socket.
quit
Close the connection when in interactive mode.
@ -2881,7 +2886,7 @@ show errors [<iid>|<proxy>] [request|response]
error was at byte 23. This is the slash ('/') in header name
"header/bizarre", which is not a valid HTTP character for a header name.
show events [<sink>] [-w] [-n]
show events [<sink>] [-w] [-n] [-0]
With no option, this lists all known event sinks and their types. With an
option, it will dump all available events in the designated sink if it is of
type buffer. If option "-w" is passed after the sink name, then once the end
@ -2890,7 +2895,9 @@ show events [<sink>] [-w] [-n]
be discarded) or by closing the session. Finally, option "-n" is used to
directly seek to the end of the buffer, which is often convenient when
combined with "-w" to only report new events. For convenience, "-wn" or "-nw"
may be used to enable both options at once.
may be used to enable both options at once. By default, all events are
delimited by a line feed character ('\n' or 10 or 0x0A). It is possible to
change this to the NUL character ('\0' or 0) by passing the "-0" argument.
show fd [-!plcfbsd]* [<fd>]
Dump the list of either all open file descriptors or just the one number <fd>
@ -3122,7 +3129,7 @@ show peers [dict|-] [<peers section>]
table:0x55871b5b46a0 id=stkt update=1 localupdate=0 \
commitupdate=0 syncing=0
show pools [byname|bysize|byusage] [match <pfx>] [<nb>]
show pools [byname|bysize|byusage] [detailed] [match <pfx>] [<nb>]
Dump the status of internal memory pools. This is useful to track memory
usage when suspecting a memory leak for example. It does exactly the same
as the SIGQUIT when running in foreground except that it does not flush the
@ -3130,10 +3137,12 @@ show pools [byname|bysize|byusage] [match <pfx>] [<nb>]
sorted by pool name; if "bysize" is specified, it is sorted by item size in
reverse order; if "byusage" is specified, it is sorted by total usage in
reverse order, and only used entries are shown. It is also possible to limit
the output to the <nb> first entries (e.g. when sorting by usage). Finally,
if "match" followed by a prefix is specified, then only pools whose name
starts with this prefix will be shown. The reported total only concerns pools
matching the filtering criteria. Example:
the output to the <nb> first entries (e.g. when sorting by usage). It is
possible to also dump more internal details, including the list of all pools
that were merged together, by specifying "detailed". Finally, if "match"
followed by a prefix is specified, then only pools whose name starts with
this prefix will be shown. The reported total only concerns pools matching
the filtering criteria. Example:
$ socat - /tmp/haproxy.sock <<< "show pools match quic byusage"
Dumping pools usage. Use SIGQUIT to flush them.
@ -3198,11 +3207,11 @@ show quic [<format>] [<filter>]
An optional argument can be specified to control the verbosity. Its value can
be interpreted in different way. The first possibility is to used predefined
values, "oneline" for the default format and "full" to display all
information. Alternatively, a list of comma-delimited fields can be specified
to restrict output. Currently supported values are "tp", "sock", "pktns",
"cc" and "mux". Finally, "help" in the format will instead show a more
detailed help message.
values, "oneline" for the default format, "stream" to list every active
streams and "full" to display all information. Alternatively, a list of
comma-delimited fields can be specified to restrict output. Currently
supported values are "tp", "sock", "pktns", "cc" and "mux". Finally, "help"
in the format will instead show a more detailed help message.
The final argument is used to restrict or extend the connection list. By
default, connections on closing or draining state are not displayed. Use the
@ -3217,7 +3226,29 @@ show servers conn [<backend>]
The output consists in a header line showing the fields titles, then one
server per line with for each, the backend name and ID, server name and ID,
the address, port and a series or values. The number of fields varies
depending on thread count.
depending on thread count. The exact format of the output may vary slightly
across versions and depending on the number of threads. One needs to pay
attention to the header line to match columns when extracting output values,
and to the number of threads as the last columns are per-thread:
bkname/svname Backend name '/' server name
bkid/svid Backend ID '/' server ID
addr Server's IP address
port Server's port (or zero if none)
- Unused field, serves as a visual delimiter
purge_delay Interval between connection purges, in milliseconds
used_cur Number of connections currently in use
used_max Highest value of used_cur since the process started
need_est Floating estimate of total needed connections
unsafe_nb Number of idle connections considered as "unsafe"
safe_nb Number of idle connections considered as "safe"
idle_lim Configured maximum number of idle connections
idle_cur Total of the per-thread currently idle connections
idle_per_thr[NB] Idle conns per thread for each one of the NB threads
HAProxy will kill a portion of <idle_cur> every <purge_delay> when the total
of <idle_cur> + <used_cur> exceeds the estimate <need_est>. This estimate
varies based on connection activity.
Given the threaded nature of idle connections, it's important to understand
that some values may change once read, and that as such, consistency within a
@ -3334,41 +3365,52 @@ show sess [<options>*]
output reports less entries than really exist because it will dump all
existing streams up to the last one that was created before the command was
entered; those which die in the mean time will not appear.
For supported opitons, see below.
For supported options, see below.
show sess [<id> | older <age> | susp | all] [<options>*]
Display a lot of internal information about the matching streams. In the
first form, only the stream matching the specified stream identifier will
be shown. This identifier is the first field at the beginning of the lines in
the dumps of "show sess" (it corresponds to the stream pointer). In the
second form, only streams older than <age> (in seconds by default) will be
shown. Passing "susp" instead will only report entries that are considered as
suspicious by the developers based on criteria that may in time or vary along
versions. If "all" is used instead, then all streams will be dumped. Dumping
many streams can produce a huge output, take a lot of time and be CPU
intensive, so it's always better to only dump the minimum needed. Those
information are useless to most users but may be used by haproxy developers
to troubleshoot a complex bug. The output format is intentionally not
documented so that it can freely evolve depending on demands. This output
is meant to be interpreted while checking function strm_dump_to_buffer() in
src/stream.c to figure the exact meaning of certain fields.
show sess [<id> | all | help] [<options>*]
Display a lot of internal information about the matching streams. The command
knows two output formats: a short one, which is the default when not asking
for a specific stream identifier, and an extended one when listing designated
streams. The short format, used by default with "show sess", only dumps one
stream per line with a few info, and the stream identifier at the beginning
of the line in hexadecimal (it corresponds to the pointer to the stream).
It is possible to set some options to customize the dump. Here are the
supported options:
In the extended form, used by "show sess <id>" or "show sess all", streams
are dumped with a huge amount of debugging details over multiple lines
(around 20 each), and still start with their identifier. The delimiter
between streams here is the identifier at the beginning of the line; extra
lines belonging to the same stream start with one or multiple spaces (the
stream is dumped indented). Dumping many streams can produce a huge output,
take a lot of time and be CPU intensive, so it's always better to only dump
the minimum needed. Those information are useless to most users but may be
used by HAProxy developers to troubleshoot a complex bug. The exact output
format is intentionally not documented so that it can freely evolve depending
on requirements, including in stable branches. This output is meant to be
interpreted while checking function strm_dump_to_buffer() in src/stream.c to
figure the exact meaning of certain fields.
- show-uri: Dump the transaction URI, as captured during the request
analysis. It is only displayed if it was captured.
The "help" argument will show the detailed usage of the command instead of
dumping streams.
- help: dump a more detailed help message instead
It is possible to set some options to customize the dump or apply some
filters. Here are the supported options:
- backend <b> only display streams attached to this backend
- frontend <f> only display streams attached to this frontend
- older <age> only display streams older than <age> seconds
- server <b/s> only show streams attached to this backend+server
- show-uri dump the transaction URI, as captured during the request
analysis. It is only displayed if it was captured.
- susp only show streams considered as suspicious by the developers
based on criteria that may in time or vary along versions.
show stat [domain <resolvers|proxy>] [{<iid>|<proxy>} <type> <sid>] \
[typed|json] [desc] [up|no-maint]
Dump statistics. The domain is used to select which statistics to print; dns
and proxy are available for now. By default, the CSV format is used; you can
activate the extended typed output format described in the section above if
"typed" is passed after the other arguments; or in JSON if "json" is passed
after the other arguments. By passing <id>, <type> and <sid>, it is possible
to dump only selected items :
Dump statistics. The domain is used to select which statistics to print;
resolvers and proxy are available for now. By default, the CSV format is used;
you can activate the extended typed output format described in the section
above if "typed" is passed after the other arguments; or in JSON if "json" is
passed after the other arguments. By passing <id>, <type> and <sid>, it is
possible to dump only selected items :
- <iid> is a proxy ID, -1 to dump everything. Alternatively, a proxy name
<proxy> may be specified. In this case, this proxy's ID will be used as
the ID selector.
@ -3775,7 +3817,7 @@ show ssl providers
- fips
- base
show ssl sni [-f <frontend>] [-A]
show ssl sni [-f <frontend>] [-A] [-t <offset>]
Dump every SNI configured for the designated frontend, or all frontends if no
frontend was specified. It allows to see what SNI are offered for a frontend,
and to identify if a SNI is defined multiple times by multiple certificates for
@ -3784,6 +3826,12 @@ show ssl sni [-f <frontend>] [-A]
The -A option allows to filter the list and only displays the certificates
that are past the notAfter date, allowing to show only expired certificates.
The -t option takes an offset in seconds, or with a time unit (s, m, h, d),
which is added to the current time, allowing to check which certificates
expired after the offset when combined with -A.
For example if you want to check which certificates would be expired in 30d,
just do "show ssl sni -A -t 30d".
Columns are separated by a single \t, allowing to parse it simply.
The 'Frontend/Bind' column shows the frontend name followed by the bind line
@ -3791,7 +3839,7 @@ show ssl sni [-f <frontend>] [-A]
The 'SNI' column shows the SNI, it can be either a CN, a SAN or a filter from a crt-list.
The default certificates of a bind line, (which are either declared
explicitely by 'default-crt' or is implicitely the first certificate of a bind
explicitly by 'default-crt' or is implicitly the first certificate of a bind
line when no 'strict-sni' is used) shows the '*' character in the SNI column.
The 'Negative Filter' column is the list of negative filters associated to a
@ -3807,7 +3855,7 @@ show ssl sni [-f <frontend>] [-A]
leaf certificate.
Example:
$ echo "@1 show ssl sni" | socat /var/run/haproxy-master.sock - | column -t -s $'\t'
$ echo "@1 show ssl sni -A -t 30d" | socat /var/run/haproxy-master.sock - | column -t -s $'\t'
# Frontend/Bind SNI Negative Filter Type Filename NotAfter NotBefore
li1/haproxy.cfg:10021 *.ex.lan !m1.ex.lan rsa example.lan.pem Jun 13 13:37:21 2024 GMT May 14 13:37:21 2024 GMT
li1/haproxy.cfg:10021 machine10 - ecdsa machine10.pem.ecdsa Jun 13 13:37:21 2024 GMT May 14 13:37:21 2024 GMT
@ -4294,6 +4342,11 @@ Example:
master. Leaving processes are only accessible with the PID as relative process
number are only usable with the current processes.
This prefix may be used as a wrapper before a command, indicating that this
command and only this one will be sent to the designated process. In this
case the full command ends at the end of line or semi-colon like any regular
command.
Examples:
$ socat /var/run/haproxy-master.sock readline
@ -4311,8 +4364,10 @@ Example:
$ echo '@!1271 show info; @!1272 show info' | socat /var/run/haproxy-master.sock -
[...]
A prefix could be use as a command, which will send every next commands to
the specified process.
The prefix may also be use as a standalone command to switch the default execution
context to the designated process, indicating that all subsequent commands will all
be executed in that process, until a new '@' command changes the execution context
again.
Examples:
@ -4329,6 +4384,52 @@ Example:
$ echo '@1; show info; show stat; @2; show info; show stat' | socat /var/run/haproxy-master.sock -
[...]
Note about limitations: a few rare commands alter a CLI session's state
(e.g. "set anon", "set timeout") and may not behave exactly similarly once
run from the master CLI due to commands being sent one at a time on their own
CLI session. Similarly, a few rare commands ("show events", "wait") actively
monitor the CLI for input or closure and are immediately interrupted when the
CLI is closed. These commands will not work as expected through the master
CLI because the command's input is closed after each command. For such rare
casesn the "@@" variant below might be more suited.
@@<[!]pid> [command...]
This prefix or command is very similar to the "@" prefix documented above
except that it enters the worker process, delivers the whole command line
into it as-is and stays there until the command finishes. Semi-colons are
delivered as well, allowing to execute a full pipelined command in a worker
process. The connection with the work remains open until the list of commands
completes. Any data sent after the commands will be forwarded to the worker
process' CLI and may be consumed by the commands being executed and will be
lost for the master process' CLI, offering a truly bidirectional connection
with the worker process. As such, users of such commands must be very careful
to wait for the command's completion before sending new commands to the
master CLI.
Instead of executing a single command, it is also possible to open a fully
interactive session on the worker process by not specifying any command
(i.e. "@@1" on its own line). This session can be terminated either by
closing the connection or by quitting the worker process (using the "quit"
command). In this case, the prompt mode of the master socket (interactive,
prompt, timed) is propagated into the worker process.
Examples:
# gracefully close connections and delete a server once idle (wait max 10s)
$ socat -t 11 /var/run/haproxy-master.sock - <<< \
"@@1 disable server app2/srv36; \
wait 10000 srv-removable app2/srv36; \
del server app2/srv36"
# forcefully close connections and quickly delete a server
$ socat /var/run/haproxy-master.sock - <<< \
"@@1 disable server app2/srv36; \
shutdown sessions server app2/srv36; \
wait 100 srv-removable app2/srv36; \
del server app2/srv36"
# show messages arriving to this ring in real time ("tail -f" equivalent)
$ (echo "show events buf0 -w"; read) | socat /var/run/haproxy-master.sock -
expert-mode [on|off]
This command activates the "expert-mode" for every worker accessed from the
master CLI. Combined with "mcli-debug-mode" it also activates the command on

14
examples/games.cfg Normal file
View File

@ -0,0 +1,14 @@
global
default-path config
tune.lua.bool-sample-conversion normal
# load all games here
lua-load lua/trisdemo.lua
defaults
timeout client 1h
# map one TCP port to each game
.notice 'use "socat TCP-CONNECT:0:7001 STDIO,raw,echo=0" to start playing'
frontend trisdemo
bind :7001
tcp-request content use-service lua.trisdemo

251
examples/lua/trisdemo.lua Normal file
View File

@ -0,0 +1,251 @@
-- Example game of falling pieces for HAProxy CLI/Applet
local board_width = 10
local board_height = 20
local game_name = "Lua Tris Demo"
-- Shapes with IDs for color mapping
local pieces = {
{id = 1, shape = {{1,1,1,1}}}, -- I (Cyan)
{id = 2, shape = {{1,1},{1,1}}}, -- O (Yellow)
{id = 3, shape = {{0,1,0},{1,1,1}}}, -- T (Purple)
{id = 4, shape = {{0,1,1},{1,1,0}}}, -- S (Green)
{id = 5, shape = {{1,1,0},{0,1,1}}}, -- Z (Red)
{id = 6, shape = {{1,0,0},{1,1,1}}}, -- J (Blue)
{id = 7, shape = {{0,0,1},{1,1,1}}} -- L (Orange)
}
-- ANSI escape codes
local clear_screen = "\27[2J"
local cursor_home = "\27[H"
local cursor_hide = "\27[?25l"
local cursor_show = "\27[?25h"
local reset_color = "\27[0m"
local color_codes = {
[1] = "\27[1;36m", -- I: Cyan
[2] = "\27[1;37m", -- O: White
[3] = "\27[1;35m", -- T: Purple
[4] = "\27[1;32m", -- S: Green
[5] = "\27[1;31m", -- Z: Red
[6] = "\27[1;34m", -- J: Blue
[7] = "\27[1;33m" -- L: Yellow
}
local function init_board()
local board = {}
for y = 1, board_height do
board[y] = {}
for x = 1, board_width do
board[y][x] = 0 -- 0 for empty, piece ID for placed blocks
end
end
return board
end
local function can_place_piece(board, piece, px, py)
for y = 1, #piece do
for x = 1, #piece[1] do
if piece[y][x] == 1 then
local board_x = px + x - 1
local board_y = py + y - 1
if board_x < 1 or board_x > board_width or board_y > board_height or
(board_y >= 1 and board[board_y][board_x] ~= 0) then
return false
end
end
end
end
return true
end
local function place_piece(board, piece, piece_id, px, py)
for y = 1, #piece do
for x = 1, #piece[1] do
if piece[y][x] == 1 then
local board_x = px + x - 1
local board_y = py + y - 1
if board_y >= 1 and board_y <= board_height then
board[board_y][board_x] = piece_id -- Store piece ID for color
end
end
end
end
end
local function clear_lines(board)
local lines_cleared = 0
local y = board_height
while y >= 1 do
local full = true
for x = 1, board_width do
if board[y][x] == 0 then
full = false
break
end
end
if full then
table.remove(board, y)
table.insert(board, 1, {})
for x = 1, board_width do
board[1][x] = 0
end
lines_cleared = lines_cleared + 1
else
y = y - 1
end
end
return lines_cleared
end
local function rotate_piece(piece, piece_id, px, py, board)
local new_piece = {}
for x = 1, #piece[1] do
new_piece[x] = {}
for y = 1, #piece do
new_piece[x][#piece + 1 - y] = piece[y][x]
end
end
if can_place_piece(board, new_piece, px, py) then
return new_piece
end
return piece
end
function render(applet, board, piece, piece_id, px, py, score)
local output = cursor_home
output = output .. game_name .. " - Lines: " .. score .. "\r\n"
output = output .. "+" .. string.rep("-", board_width * 2) .. "+\r\n"
for y = 1, board_height do
output = output .. "|"
for x = 1, board_width do
local char = " "
-- Current piece
for py_idx = 1, #piece do
for px_idx = 1, #piece[1] do
if piece[py_idx][px_idx] == 1 then
local board_x = px + px_idx - 1
local board_y = py + py_idx - 1
if board_x == x and board_y == y then
char = color_codes[piece_id] .. "[]" .. reset_color
end
end
end
end
-- Placed blocks
if board[y][x] ~= 0 then
char = color_codes[board[y][x]] .. "[]" .. reset_color
end
output = output .. char
end
output = output .. "|\r\n"
end
output = output .. "+" .. string.rep("-", board_width * 2) .. "+\r\n"
output = output .. "Use arrow keys to move, Up to rotate, q to quit"
applet:send(output)
end
function handler(applet)
local board = init_board()
local piece_idx = math.random(#pieces)
local current_piece = pieces[piece_idx].shape
local piece_id = pieces[piece_idx].id
local piece_x = math.floor(board_width / 2) - math.floor(#current_piece[1] / 2)
local piece_y = 1
local score = 0
local game_over = false
local delay = 500
if not can_place_piece(board, current_piece, piece_x, piece_y) then
game_over = true
end
applet:send(cursor_hide)
applet:send(clear_screen)
-- fall the piece by one line every delay
local function fall_piece()
while not game_over do
piece_y = piece_y + 1
if not can_place_piece(board, current_piece, piece_x, piece_y) then
piece_y = piece_y - 1
place_piece(board, current_piece, piece_id, piece_x, piece_y)
score = score + clear_lines(board)
piece_idx = math.random(#pieces)
current_piece = pieces[piece_idx].shape
piece_id = pieces[piece_idx].id
piece_x = math.floor(board_width / 2) - math.floor(#current_piece[1] / 2)
piece_y = 1
if not can_place_piece(board, current_piece, piece_x, piece_y) then
game_over = true
end
end
core.msleep(delay)
end
end
core.register_task(fall_piece)
local function drop_piece()
while can_place_piece(board, current_piece, piece_x, piece_y) do
piece_y = piece_y + 1
end
piece_y = piece_y - 1
place_piece(board, current_piece, piece_id, piece_x, piece_y)
score = score + clear_lines(board)
piece_idx = math.random(#pieces)
current_piece = pieces[piece_idx].shape
piece_id = pieces[piece_idx].id
piece_x = math.floor(board_width / 2) - math.floor(#current_piece[1] / 2)
piece_y = 1
if not can_place_piece(board, current_piece, piece_x, piece_y) then
game_over = true
end
render(applet, board, current_piece, piece_id, piece_x, piece_y, score)
end
while not game_over do
render(applet, board, current_piece, piece_id, piece_x, piece_y, score)
-- update the delay based on the score: 500 for 0 lines to 100ms for 100 lines.
if score >= 100 then
delay = 100
else
delay = 500 - 4*score
end
local input = applet:receive(1, delay)
if input then
if input == "" or input == "q" then
game_over = true
elseif input == "\27" then
local a = applet:receive(1, delay)
if a == "[" then
local b = applet:receive(1, delay)
if b == "A" then -- Up arrow (rotate clockwise)
current_piece = rotate_piece(current_piece, piece_id, piece_x, piece_y, board)
elseif b == "B" then -- Down arrow (full drop)
drop_piece()
elseif b == "C" then -- Right arrow
piece_x = piece_x + 1
if not can_place_piece(board, current_piece, piece_x, piece_y) then
piece_x = piece_x - 1
end
elseif b == "D" then -- Left arrow
piece_x = piece_x - 1
if not can_place_piece(board, current_piece, piece_x, piece_y) then
piece_x = piece_x + 1
end
end
end
end
end
end
applet:send(clear_screen .. cursor_home .. "Game Over! Lines: " .. score .. "\r\n" .. cursor_show)
end
-- works as a TCP applet
core.register_service("trisdemo", "tcp", handler)
-- may also work on the CLI but requires an unbuffered handler
core.register_cli({"trisdemo"}, "Play a simple falling pieces game", handler)

84
include/haproxy/acme-t.h Normal file
View File

@ -0,0 +1,84 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#ifndef _ACME_T_H_
#define _ACME_T_H_
#include <haproxy/istbuf.h>
#include <haproxy/openssl-compat.h>
#define ACME_RETRY 5
/* acme section configuration */
struct acme_cfg {
char *filename; /* config filename */
int linenum; /* config linenum */
char *name; /* section name */
char *directory; /* directory URL */
char *map; /* storage for tokens + thumbprint */
struct {
char *contact; /* email associated to account */
char *file; /* account key filename */
EVP_PKEY *pkey; /* account PKEY */
char *thumbprint; /* account PKEY JWS thumbprint */
} account;
struct {
int type; /* EVP_PKEY_EC or EVP_PKEY_RSA */
int bits; /* bits for RSA */
int curves; /* NID of curves */
} key;
char *challenge; /* HTTP-01, DNS-01, etc */
struct acme_cfg *next;
};
enum acme_st {
ACME_RESSOURCES = 0,
ACME_NEWNONCE,
ACME_CHKACCOUNT,
ACME_NEWACCOUNT,
ACME_NEWORDER,
ACME_AUTH,
ACME_CHALLENGE,
ACME_CHKCHALLENGE,
ACME_FINALIZE,
ACME_CHKORDER,
ACME_CERTIFICATE,
ACME_END
};
enum http_st {
ACME_HTTP_REQ,
ACME_HTTP_RES,
};
struct acme_auth {
struct ist auth; /* auth URI */
struct ist chall; /* challenge URI */
struct ist token; /* token */
void *next;
};
/* acme task context */
struct acme_ctx {
enum acme_st state;
enum http_st http_state;
int retries;
int retryafter;
struct httpclient *hc;
struct acme_cfg *cfg;
struct ckch_store *store;
struct {
struct ist newNonce;
struct ist newAccount;
struct ist newOrder;
} ressources;
struct ist nonce;
struct ist kid;
struct ist order;
struct acme_auth *auths;
struct acme_auth *next_auth;
X509_REQ *req;
struct ist finalize;
struct ist certificate;
struct mt_list el;
};
#endif

9
include/haproxy/acme.h Normal file
View File

@ -0,0 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#ifndef _ACME_H_
#define _ACME_H_
#include <haproxy/ssl_ckch-t.h>
int ckch_conf_acme_init(void *value, char *buf, struct ckch_data *d, int cli, const char *filename, int linenum, char **err);
#endif

View File

@ -101,29 +101,36 @@ struct applet {
struct appctx {
enum obj_type obj_type; /* OBJ_TYPE_APPCTX */
/* 3 unused bytes here */
unsigned int st0; /* CLI state for stats, session state for peers */
unsigned int st1; /* prompt/payload (bitwise OR of APPCTX_CLI_ST1_*) for stats, session error for peers */
unsigned int st0; /* Main applet state. May be used by any applet */
unsigned int st1; /* Applet substate. Mau be used by any applet */
unsigned int flags; /* APPCTX_FL_* */
struct buffer inbuf;
struct buffer outbuf;
size_t to_forward;
struct buffer *chunk; /* used to store unfinished commands */
struct applet *applet; /* applet this context refers to */
struct session *sess; /* session for frontend applets (NULL for backend applets) */
struct sedesc *sedesc; /* stream endpoint descriptor the applet is attached to */
struct act_rule *rule; /* rule associated with the applet. */
int (*io_handler)(struct appctx *appctx); /* used within the cli_io_handler when st0 = CLI_ST_CALLBACK */
void (*io_release)(struct appctx *appctx); /* used within the cli_io_handler when st0 = CLI_ST_CALLBACK,
if the command is terminated or the session released */
int cli_severity_output; /* used within the cli_io_handler to format severity output of informational feedback */
int cli_level; /* the level of CLI which can be lowered dynamically */
char cli_payload_pat[8]; /* Payload pattern */
uint32_t cli_anon_key; /* the key to anonymise with the hash in cli */
struct {
struct buffer *cmdline; /* used to store unfinished commands */
int severity_output; /* used within the cli_io_handler to format severity output of informational feedback */
int level; /* the level of CLI which can be lowered dynamically */
char payload_pat[8]; /* Payload pattern */
char *payload; /* Pointer on the payload. NULL if no payload */
uint32_t anon_key; /* the key to anonymise with the hash in cli */
/* XXX 4 unused bytes here */
int (*io_handler)(struct appctx *appctx); /* used within the cli_io_handler when st0 = CLI_ST_CALLBACK */
void (*io_release)(struct appctx *appctx); /* used within the cli_io_handler when st0 = CLI_ST_CALLBACK,
if the command is terminated or the session released */
} cli_ctx; /* context dedicated to the CLI applet */
struct buffer_wait buffer_wait; /* position in the list of objects waiting for a buffer */
struct task *t; /* task associated to the applet */
struct freq_ctr call_rate; /* appctx call rate */
/* XXX 4 unused bytes here */
struct mt_list wait_entry; /* entry in a list of waiters for an event (e.g. ring events) */
/* The pointer seen by application code is appctx->svcctx. In 2.7 the

View File

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

View File

@ -26,9 +26,9 @@
#include <haproxy/compiler.h>
/* A few notes for the macros and functions here:
* - this file is painful to edit, most operations exist in 3 variants,
* no-thread, threads with gcc<4.7, threads with gcc>=4.7. Be careful when
* modifying it not to break any of them.
* - this file is painful to edit, most operations exist in 2 variants,
* no-thread, and threads (with gcc>=4.7). Be careful when modifying
* it not to break any of them.
*
* - macros named HA_ATOMIC_* are or use in the general case, they contain the
* required memory barriers to guarantee sequential consistency
@ -191,122 +191,10 @@
/* Threads are ENABLED, all atomic ops are made thread-safe. By extension they
* can also be used for inter-process synchronization but one must verify that
* the code still builds with threads disabled.
* the code still builds with threads disabled. Code below requires C11 atomics
* as present in gcc >= 4.7 or clang.
*/
#if defined(__GNUC__) && (__GNUC__ < 4 || __GNUC__ == 4 && __GNUC_MINOR__ < 7) && !defined(__clang__)
/* gcc < 4.7 */
#define HA_ATOMIC_LOAD(val) \
({ \
typeof((val)) __val_load = (val); \
typeof(*(val)) __ret_val = \
({ __sync_synchronize(); *(volatile typeof(__val_load))__val_load; }); \
__sync_synchronize(); \
__ret_val; \
})
#define HA_ATOMIC_STORE(val, new) \
({ \
typeof((val)) __val_store = (val); \
typeof(*(val)) __old_store; \
typeof((new)) __new_store = (new); \
do { __old_store = *__val_store; \
} while (!__sync_bool_compare_and_swap(__val_store, __old_store, __new_store) && __ha_cpu_relax()); \
})
#define HA_ATOMIC_XCHG(val, new) \
({ \
typeof((val)) __val_xchg = (val); \
typeof(*(val)) __old_xchg; \
typeof((new)) __new_xchg = (new); \
do { __old_xchg = *__val_xchg; \
} while (!__sync_bool_compare_and_swap(__val_xchg, __old_xchg, __new_xchg) && __ha_cpu_relax()); \
__old_xchg; \
})
#define HA_ATOMIC_AND(val, flags) do { __sync_and_and_fetch(val, flags); } while (0)
#define HA_ATOMIC_OR(val, flags) do { __sync_or_and_fetch(val, flags); } while (0)
#define HA_ATOMIC_ADD(val, i) do { __sync_add_and_fetch(val, i); } while (0)
#define HA_ATOMIC_SUB(val, i) do { __sync_sub_and_fetch(val, i); } while (0)
#define HA_ATOMIC_INC(val) do { __sync_add_and_fetch(val, 1); } while (0)
#define HA_ATOMIC_DEC(val) do { __sync_sub_and_fetch(val, 1); } while (0)
#define HA_ATOMIC_AND_FETCH(val, flags) __sync_and_and_fetch(val, flags)
#define HA_ATOMIC_OR_FETCH(val, flags) __sync_or_and_fetch(val, flags)
#define HA_ATOMIC_ADD_FETCH(val, i) __sync_add_and_fetch(val, i)
#define HA_ATOMIC_SUB_FETCH(val, i) __sync_sub_and_fetch(val, i)
#define HA_ATOMIC_FETCH_AND(val, flags) __sync_fetch_and_and(val, flags)
#define HA_ATOMIC_FETCH_OR(val, flags) __sync_fetch_and_or(val, flags)
#define HA_ATOMIC_FETCH_ADD(val, i) __sync_fetch_and_add(val, i)
#define HA_ATOMIC_FETCH_SUB(val, i) __sync_fetch_and_sub(val, i)
#define HA_ATOMIC_BTS(val, bit) \
({ \
typeof(*(val)) __b_bts = (1UL << (bit)); \
__sync_fetch_and_or((val), __b_bts) & __b_bts; \
})
#define HA_ATOMIC_BTR(val, bit) \
({ \
typeof(*(val)) __b_btr = (1UL << (bit)); \
__sync_fetch_and_and((val), ~__b_btr) & __b_btr; \
})
/* the CAS is a bit complicated. The older API doesn't support returning the
* value and the swap's result at the same time. So here we take what looks
* like the safest route, consisting in using the boolean version guaranteeing
* that the operation was performed or not, and we snoop a previous value. If
* the compare succeeds, we return. If it fails, we return the previous value,
* but only if it differs from the expected one. If it's the same it's a race
* thus we try again to avoid confusing a possibly sensitive caller.
*/
#define HA_ATOMIC_CAS(val, old, new) \
({ \
typeof((val)) __val_cas = (val); \
typeof((old)) __oldp_cas = (old); \
typeof(*(old)) __oldv_cas; \
typeof((new)) __new_cas = (new); \
int __ret_cas; \
do { \
__oldv_cas = *__val_cas; \
__ret_cas = __sync_bool_compare_and_swap(__val_cas, *__oldp_cas, __new_cas); \
} while (!__ret_cas && *__oldp_cas == __oldv_cas && __ha_cpu_relax()); \
if (!__ret_cas) \
*__oldp_cas = __oldv_cas; \
__ret_cas; \
})
/* warning, n is a pointer to the double value for dwcas */
#define HA_ATOMIC_DWCAS(val, o, n) __ha_cas_dw(val, o, n)
#define HA_ATOMIC_UPDATE_MAX(val, new) \
({ \
typeof(val) __val = (val); \
typeof(*(val)) __old_max = *__val; \
typeof(*(val)) __new_max = (new); \
\
while (__old_max < __new_max && \
!HA_ATOMIC_CAS(__val, &__old_max, __new_max) && __ha_cpu_relax()); \
*__val; \
})
#define HA_ATOMIC_UPDATE_MIN(val, new) \
({ \
typeof(val) __val = (val); \
typeof(*(val)) __old_min = *__val; \
typeof(*(val)) __new_min = (new); \
\
while (__old_min > __new_min && \
!HA_ATOMIC_CAS(__val, &__old_min, __new_min) && __ha_cpu_relax()); \
*__val; \
})
#else /* gcc */
/* gcc >= 4.7 or clang */
#define HA_ATOMIC_STORE(val, new) __atomic_store_n(val, new, __ATOMIC_RELEASE)
#define HA_ATOMIC_LOAD(val) __atomic_load_n(val, __ATOMIC_ACQUIRE)
#define HA_ATOMIC_XCHG(val, new) __atomic_exchange_n(val, new, __ATOMIC_ACQ_REL)
@ -520,8 +408,6 @@
/* warning, n is a pointer to the double value for dwcas */
#define _HA_ATOMIC_DWCAS(val, o, n) __ha_cas_dw(val, o, n)
#endif /* gcc >= 4.7 */
/* Here come a few architecture-specific double-word CAS and barrier
* implementations.
*/
@ -799,12 +685,13 @@ static __inline int __ha_cas_dw(void *target, void *compare, void *set)
#else /* unknown / unhandled architecture, fall back to generic barriers */
#define __ha_barrier_atomic_load __sync_synchronize
#define __ha_barrier_atomic_store __sync_synchronize
#define __ha_barrier_atomic_full __sync_synchronize
#define __ha_barrier_load __sync_synchronize
#define __ha_barrier_store __sync_synchronize
#define __ha_barrier_full __sync_synchronize
#define __ha_barrier_atomic_load() __atomic_thread_fence(__ATOMIC_ACQUIRE)
#define __ha_barrier_atomic_store() __atomic_thread_fence(__ATOMIC_RELEASE)
#define __ha_barrier_atomic_full() __atomic_thread_fence(__ATOMIC_SEQ_CST)
#define __ha_barrier_load() __atomic_thread_fence(__ATOMIC_ACQUIRE)
#define __ha_barrier_store() __atomic_thread_fence(__ATOMIC_RELEASE)
#define __ha_barrier_full() __atomic_thread_fence(__ATOMIC_SEQ_CST)
/* Note: there is no generic DWCAS */
/* short-lived CPU relaxation */

View File

@ -144,6 +144,12 @@
*/
#define BE_WEIGHT_SCALE 16
/* LB parameters for all algorithms, with one instance per thread-group */
struct lbprm_per_tgrp {
union {
struct lb_fwrr_per_tgrp fwrr;
};
};
/* LB parameters for all algorithms */
struct lbprm {
union { /* LB parameters depending on the algo type */
@ -162,12 +168,15 @@ struct lbprm {
int wmult; /* ratio between user weight and effective weight */
int wdiv; /* ratio between effective weight and user weight */
int hash_balance_factor; /* load balancing factor * 100, 0 if disabled */
unsigned int lb_free_list_nb; /* Number of elements in the free list */
struct sample_expr *expr; /* sample expression for "balance (log-)hash" */
char *arg_str; /* name of the URL parameter/header/cookie used for hashing */
int arg_len; /* strlen(arg_str), computed only once */
int arg_opt1; /* extra option 1 for the LB algo (algo-specific) */
int arg_opt2; /* extra option 2 for the LB algo (algo-specific) */
int arg_opt3; /* extra option 3 for the LB algo (algo-specific) */
uint64_t lb_seq; /* sequence number for algos who need it */
struct mt_list lb_free_list; /* LB tree elements available */
__decl_thread(HA_RWLOCK_T lock);
struct server *fbck; /* first backup server when !PR_O_USE_ALL_BK, or NULL */
@ -181,6 +190,8 @@ struct lbprm {
void (*server_take_conn)(struct server *); /* to be called when connection is assigned */
void (*server_drop_conn)(struct server *); /* to be called when connection is dropped */
void (*server_requeue)(struct server *); /* function used to place the server where it must be */
void (*proxy_deinit)(struct proxy *); /* to be called when we're destroying the proxy */
void (*server_deinit)(struct server *); /* to be called when we're destroying the server */
};
#endif /* _HAPROXY_BACKEND_T_H */

View File

@ -45,6 +45,16 @@ int assign_server_and_queue(struct stream *s);
int alloc_bind_address(struct sockaddr_storage **ss,
struct server *srv, struct proxy *be,
struct stream *s);
int64_t be_calculate_conn_hash(struct server *srv, struct stream *strm,
struct session *sess,
struct sockaddr_storage *src,
struct sockaddr_storage *dst,
struct ist name);
int be_reuse_connection(int64_t hash, struct session *sess,
struct proxy *be, struct server *srv,
struct stconn *sc, enum obj_type *target, int not_first_req);
int srv_redispatch_connect(struct stream *t);
void back_try_conn_req(struct stream *s);
void back_handle_st_req(struct stream *s);
@ -76,7 +86,7 @@ static inline int be_usable_srv(struct proxy *be)
/* set the time of last session on the backend */
static inline void be_set_sess_last(struct proxy *be)
{
be->be_counters.last_sess = ns_to_sec(now_ns);
HA_ATOMIC_STORE(&be->be_counters.shared->tg[tgid - 1]->last_sess, ns_to_sec(now_ns));
}
/* This function returns non-zero if the designated server will be

View File

@ -68,7 +68,7 @@
#else // not x86
/* generic implementation, causes a segfault */
static inline __attribute((always_inline)) void ha_crash_now(void)
static inline __attribute((always_inline,noreturn,unused)) void ha_crash_now(void)
{
#if __GNUC_PREREQ__(5, 0)
#pragma GCC diagnostic push
@ -161,6 +161,8 @@ static __attribute__((noinline,noreturn,unused)) void abort_with_line(uint line)
* COUNT_IF() invocation requires a special section ("dbg_cnt") hence a modern
* linker.
*/
extern unsigned int debug_enable_counters;
#if !defined(USE_OBSOLETE_LINKER)
/* type of checks that can be verified. We cannot really distinguish between
@ -222,30 +224,44 @@ extern __attribute__((__weak__)) struct debug_count __stop_dbg_cnt HA_SECTION_S
_HA_ATOMIC_INC(&__dbg_cnt_##_line.count); \
} while (0)
/* Core of the COUNT_IF() macro, checks the condition and counts one hit if
* true.
*/
#define _COUNT_IF(cond, file, line, ...) \
(unlikely(cond) ? ({ \
__DBG_COUNT(cond, file, line, DBG_COUNT_IF, __VA_ARGS__); \
1; /* let's return the true condition */ \
}) : 0)
/* DEBUG_GLITCHES enables counting the number of glitches per line of code. The
/* Matrix for DEBUG_COUNTERS:
* 0 : only BUG_ON() and CHECK_IF() are reported (super rare)
* 1 : COUNT_GLITCH() are also reported (rare)
* COUNT_IF() are also reported only if debug_enable_counters was set
* 2 : COUNT_IF() are also reported unless debug_enable_counters was reset
*/
/* Core of the COUNT_IF() macro, checks the condition and counts one hit if
* true. It's only enabled at DEBUG_COUNTERS >= 1, and enabled by default if
* DEBUG_COUNTERS >= 2.
*/
# if defined(DEBUG_COUNTERS) && (DEBUG_COUNTERS >= 1)
# define _COUNT_IF(cond, file, line, ...) \
(unlikely(cond) ? ({ \
if (debug_enable_counters) \
__DBG_COUNT(cond, file, line, DBG_COUNT_IF, __VA_ARGS__); \
1; /* let's return the true condition */ \
}) : 0)
# else
# define _COUNT_IF(cond, file, line, ...) DISGUISE(unlikely(cond) ? 1 : 0)
# endif
/* DEBUG_COUNTERS enables counting the number of glitches per line of code. The
* condition is empty (nothing to write there), except maybe __VA_ARGS at the
* end.
*/
# if !defined(DEBUG_GLITCHES)
# if !defined(DEBUG_COUNTERS) || (DEBUG_COUNTERS == 0)
# define _COUNT_GLITCH(file, line, ...) do { } while (0)
# else
# define _COUNT_GLITCH(file, line, ...) do { \
__DBG_COUNT(, file, line, DBG_GLITCH, __VA_ARGS__); \
} while (0)
# endif
# endif
#else /* USE_OBSOLETE_LINKER not defined below */
# define __DBG_COUNT(cond, file, line, type, ...) do { } while (0)
# define _COUNT_IF(cond, file, line, ...) DISGUISE(cond)
# define _COUNT_IF(cond, file, line, ...) DISGUISE(unlikely(cond) ? 1 : 0)
# define _COUNT_GLITCH(file, line, ...) do { } while (0)
#endif

View File

@ -1,46 +0,0 @@
/*
* include/haprox/cbuf-t.h
* This file contains definition for circular buffers.
*
* Copyright 2021 HAProxy Technologies, Frederic Lecaille <flecaille@haproxy.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, version 2.1
* exclusively.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_CBUF_T_H
#define _HAPROXY_CBUF_T_H
#ifdef USE_QUIC
#ifndef USE_OPENSSL
#error "Must define USE_OPENSSL"
#endif
#endif
#include <stddef.h>
#include <haproxy/list-t.h>
extern struct pool_head *pool_head_cbuf;
struct cbuf {
/* buffer */
unsigned char *buf;
/* buffer size */
size_t sz;
/* Writer index */
size_t wr;
/* Reader index */
size_t rd;
};
#endif /* _HAPROXY_CBUF_T_H */

View File

@ -1,136 +0,0 @@
/*
* include/haprox/cbuf.h
* This file contains definitions and prototypes for circular buffers.
* Inspired from Linux circular buffers (include/linux/circ_buf.h).
*
* Copyright 2021 HAProxy Technologies, Frederic Lecaille <flecaille@haproxy.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, version 2.1
* exclusively.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_CBUF_H
#define _HAPROXY_CBUF_H
#ifdef USE_QUIC
#ifndef USE_OPENSSL
#error "Must define USE_OPENSSL"
#endif
#endif
#include <haproxy/atomic.h>
#include <haproxy/list.h>
#include <haproxy/cbuf-t.h>
struct cbuf *cbuf_new(unsigned char *buf, size_t sz);
void cbuf_free(struct cbuf *cbuf);
/* Amount of data between <rd> and <wr> */
#define CBUF_DATA(wr, rd, size) (((wr) - (rd)) & ((size) - 1))
/* Return the writer position in <cbuf>.
* To be used only by the writer!
*/
static inline unsigned char *cb_wr(struct cbuf *cbuf)
{
return cbuf->buf + cbuf->wr;
}
/* Reset the reader index.
* To be used by a reader!
*/
static inline void cb_rd_reset(struct cbuf *cbuf)
{
cbuf->rd = 0;
}
/* Reset the writer index.
* To be used by a writer!
*/
static inline void cb_wr_reset(struct cbuf *cbuf)
{
cbuf->wr = 0;
}
/* Increase <cbuf> circular buffer data by <count>.
* To be used by a writer!
*/
static inline void cb_add(struct cbuf *cbuf, size_t count)
{
cbuf->wr = (cbuf->wr + count) & (cbuf->sz - 1);
}
/* Return the reader position in <cbuf>.
* To be used only by the reader!
*/
static inline unsigned char *cb_rd(struct cbuf *cbuf)
{
return cbuf->buf + cbuf->rd;
}
/* Skip <count> byte in <cbuf> circular buffer.
* To be used by a reader!
*/
static inline void cb_del(struct cbuf *cbuf, size_t count)
{
cbuf->rd = (cbuf->rd + count) & (cbuf->sz - 1);
}
/* Return the amount of data left in <cbuf>.
* To be used only by the writer!
*/
static inline size_t cb_data(struct cbuf *cbuf)
{
size_t rd;
rd = HA_ATOMIC_LOAD(&cbuf->rd);
return CBUF_DATA(cbuf->wr, rd, cbuf->sz);
}
/* Return the amount of room left in <cbuf> minus 1 to distinguish
* the case where the buffer is full from the case where is is empty
* To be used only by the write!
*/
static inline size_t cb_room(struct cbuf *cbuf)
{
size_t rd;
rd = HA_ATOMIC_LOAD(&cbuf->rd);
return CBUF_DATA(rd, cbuf->wr + 1, cbuf->sz);
}
/* Return the amount of contiguous data left in <cbuf>.
* To be used only by the reader!
*/
static inline size_t cb_contig_data(struct cbuf *cbuf)
{
size_t end, n;
end = cbuf->sz - cbuf->rd;
n = (HA_ATOMIC_LOAD(&cbuf->wr) + end) & (cbuf->sz - 1);
return n < end ? n : end;
}
/* Return the amount of contiguous space left in <cbuf>.
* To be used only by the writer!
*/
static inline size_t cb_contig_space(struct cbuf *cbuf)
{
size_t end, n;
end = cbuf->sz - 1 - cbuf->wr;
n = (HA_ATOMIC_LOAD(&cbuf->rd) + end) & (cbuf->sz - 1);
return n <= end ? n : end + 1;
}
#endif /* _HAPROXY_CBUF_H */

View File

@ -23,6 +23,7 @@
#define _HAPROXY_CFGPARSE_H
#include <haproxy/api.h>
#include <haproxy/proxy-t.h>
struct hap_cpuset;
struct proxy;
@ -38,6 +39,7 @@ struct acl_cond;
#define CFG_CRTLIST 5
#define CFG_CRTSTORE 6
#define CFG_TRACES 7
#define CFG_ACME 8
/* various keyword modifiers */
enum kw_mod {
@ -109,9 +111,14 @@ extern char *cursection;
extern int non_global_section_parsed;
extern struct proxy *curproxy;
extern char initial_cwd[PATH_MAX];
int cfg_parse_global(const char *file, int linenum, char **args, int inv);
int cfg_parse_listen(const char *file, int linenum, char **args, int inv);
int cfg_parse_listen_match_option(const char *file, int linenum, int kwm,
const struct cfg_opt config_opts[], int *err_code,
char **args, int mode, int cap,
int *options, int *no_options);
int cfg_parse_traces(const char *file, int linenum, char **args, int inv);
int cfg_parse_track_sc_num(unsigned int *track_sc_num,
const char *arg, const char *end, char **err);

View File

@ -172,6 +172,7 @@ struct check {
char desc[HCHK_DESC_LEN]; /* health check description */
signed char use_ssl; /* use SSL for health checks (1: on, 0: server mode, -1: off) */
int send_proxy; /* send a PROXY protocol header with checks */
int reuse_pool; /* try to reuse idle connections */
struct tcpcheck_rules *tcpcheck_rules; /* tcp-check send / expect rules */
struct tcpcheck_rule *current_step; /* current step when using tcpcheck */
int inter, fastinter, downinter; /* checks: time in milliseconds */
@ -187,6 +188,7 @@ struct check {
char **envp; /* the environment to use if running a process-based check */
struct pid_list *curpid; /* entry in pid_list used for current process-based test, or -1 if not in test */
struct sockaddr_storage addr; /* the address to check */
char *pool_conn_name; /* conn name used on reuse */
char *sni; /* Server name */
char *alpn_str; /* ALPN to use for checks */
int alpn_len; /* ALPN string length */

View File

@ -24,7 +24,7 @@
#include <haproxy/applet-t.h>
/* Access level for a stats socket (appctx->cli_level) */
/* Access level for a stats socket (appctx->cli_ctx.level) */
#define ACCESS_LVL_NONE 0x0000
#define ACCESS_LVL_USER 0x0001
#define ACCESS_LVL_OPER 0x0002
@ -41,11 +41,12 @@
#define ACCESS_MCLI_SEVERITY_STR 0x0200 /* 'set severity-output string' on master CLI */
/* flags for appctx->st1 */
#define APPCTX_CLI_ST1_PROMPT (1 << 0)
#define APPCTX_CLI_ST1_PAYLOAD (1 << 1)
#define APPCTX_CLI_ST1_NOLF (1 << 2)
#define APPCTX_CLI_ST1_TIMED (1 << 3)
#define APPCTX_CLI_ST1_LASTCMD (1 << 4)
#define APPCTX_CLI_ST1_PAYLOAD (1 << 0)
#define APPCTX_CLI_ST1_NOLF (1 << 1)
#define APPCTX_CLI_ST1_LASTCMD (1 << 2)
#define APPCTX_CLI_ST1_INTER (1 << 3) /* interactive mode (i.e. don't close after 1st cmd) */
#define APPCTX_CLI_ST1_PROMPT (1 << 4) /* display prompt */
#define APPCTX_CLI_ST1_TIMED (1 << 5) /* display timer in prompt */
#define CLI_PREFIX_KW_NB 5
#define CLI_MAX_MATCHES 5
@ -55,8 +56,8 @@
enum {
CLI_ST_INIT = 0, /* initial state, must leave to zero ! */
CLI_ST_END, /* final state, let's close */
CLI_ST_GETREQ, /* wait for a request */
CLI_ST_PARSEREQ, /* parse a request */
CLI_ST_PARSE_CMDLINE, /* wait for a full command line */
CLI_ST_PROCESS_CMDLINE, /* process all commands on the command line */
CLI_ST_OUTPUT, /* all states after this one are responses */
CLI_ST_PROMPT, /* display the prompt (first output, same code) */
CLI_ST_PRINT, /* display const message in cli->msg */

View File

@ -55,6 +55,24 @@
#define ___equals_1(x) ____equals_1(comma_for_one ## x 1)
#define __equals_1(x) ___equals_1(x)
/* same but checks if defined as zero, useful to distinguish between -DFOO and
* -DFOO=0.
*/
#define comma_for_zero0 ,
#define _____equals_0(x, y, ...) (y)
#define ____equals_0(x, ...) _____equals_0(x, 0)
#define ___equals_0(x) ____equals_0(comma_for_zero ## x 1)
#define __equals_0(x) ___equals_0(x)
/* same but checks if defined as empty, useful to distinguish between -DFOO= and
* -DFOO=anything.
*/
#define comma_for_empty ,
#define _____def_as_empty(x, y, ...) (y)
#define ____def_as_empty(x, ...) _____def_as_empty(x, 0)
#define ___def_as_empty(x) ____def_as_empty(comma_for_empty ## x 1)
#define __def_as_empty(x) ___def_as_empty(x)
/* gcc 5 and clang 3 brought __has_attribute(), which is not well documented in
* the case of gcc, but is convenient since handled at the preprocessor level.
* In both cases it's possible to test for __has_attribute() using ifdef. When
@ -283,6 +301,11 @@
#define _TOSTR(x) #x
#define TOSTR(x) _TOSTR(x)
/* concatenates the two strings after resolving possible macros */
#undef CONCAT // Turns out NetBSD defines it to the same in exec_elf.h
#define _CONCAT(a,b) a ## b
#define CONCAT(a,b) _CONCAT(a,b)
/*
* Gcc >= 3 provides the ability for the program to give hints to the
* compiler about what branch of an if is most likely to be taken. This
@ -499,6 +522,18 @@
#define __decl_thread(decl)
#endif
/* The __decl_thread_var() statement declares a variable when threads are enabled
* or replaces it with an dummy statement to avoid placing a lone semi-colon. The
* purpose is to condition the presence of some variables or to the fact that
* threads are enabled, without having to enclose them inside an ugly
* #ifdef USE_THREAD/#endif clause.
*/
#ifdef USE_THREAD
#define __decl_thread_var(decl) decl
#else
#define __decl_thread_var(decl) enum { CONCAT(_dummy_var_decl_,__LINE__), }
#endif
/* clang has a __has_feature() macro which reports true/false on a number of
* internally supported features. Let's make sure this macro is always defined
* and returns zero when not supported.
@ -507,4 +542,15 @@
#define __has_feature(x) 0
#endif
/* gcc 15 throws warning if fixed-size char array does not contain a terminating
* NUL. gcc has an attribute 'nonstring', which allows to suppress this warning
* for such array declarations. But it's not the case for clang and other
* compilers.
*/
#if __has_attribute(nonstring)
#define __nonstring __attribute__ ((nonstring))
#else
#define __nonstring
#endif
#endif /* _HAPROXY_COMPILER_H */

View File

@ -50,6 +50,8 @@ struct comp {
struct comp_algo *algo_req; /* Algo to use for request */
struct comp_type *types_req; /* Types to be compressed for requests */
struct comp_type *types_res; /* Types to be compressed for responses */
unsigned int minsize_res; /* Min response body size to be compressed */
unsigned int minsize_req; /* Min request body size to be compressed */
unsigned int flags;
};

View File

@ -332,7 +332,7 @@ enum proto_proxy_side {
/* ctl command used by mux->ctl() */
enum mux_ctl_type {
MUX_CTL_STATUS, /* Expects an int as output, sets it to a combinaison of MUX_CTL_STATUS flags */
MUX_CTL_STATUS, /* Expects an int as output, sets it to a combination of MUX_CTL_STATUS flags */
MUX_CTL_EXIT_STATUS, /* Expects an int as output, sets the mux exist/error/http status, if known or 0 */
MUX_CTL_REVERSE_CONN, /* Notify about an active reverse connection accepted. */
MUX_CTL_SUBS_RECV, /* Notify the mux it must wait for read events again */

View File

@ -75,7 +75,7 @@ int conn_send_socks4_proxy_request(struct connection *conn);
int conn_recv_socks4_proxy_response(struct connection *conn);
/* If we delayed the mux creation because we were waiting for the handshake, do it now */
int conn_create_mux(struct connection *conn);
int conn_create_mux(struct connection *conn, int *closed_connection);
int conn_notify_mux(struct connection *conn, int old_flags, int forced_wake);
int conn_upgrade_mux_fe(struct connection *conn, void *ctx, struct buffer *buf,
struct ist mux_proto, int mode);
@ -708,6 +708,19 @@ static inline void conn_set_reverse(struct connection *conn, enum obj_type *targ
conn->reverse.target = target;
}
/* Returns idle-ping value for <conn> depending on its proxy side. */
static inline int conn_idle_ping(const struct connection *conn)
{
if (conn_is_back(conn)) {
struct server *srv = objt_server(conn->target);
return srv ? srv->idle_ping : TICK_ETERNITY;
}
else {
struct session *sess = conn->owner;
return sess->listener->bind_conf->idle_ping;
}
}
/* Returns the listener instance for connection used for active reverse. */
static inline struct listener *conn_active_reverse_listener(const struct connection *conn)
{
@ -784,7 +797,7 @@ static inline const char *tevt_evts2str(uint32_t evts)
if (!evt)
continue;
/* Backend location are displayed in captial letter */
/* Backend location are displayed in capital letter */
is_back = !!((evt >> 4) & 0x8);
switch ((enum term_event_loc)((evt >> 4) & ~0x8)) {
case tevt_loc_fd: tevt_evts_str[idx++] = (is_back ? 'F' : 'f'); break;

View File

@ -25,108 +25,144 @@
#include <haproxy/freq_ctr-t.h>
#define COUNTERS_SHARED_F_NONE 0x0000
#define COUNTERS_SHARED_F_LOCAL 0x0001 // shared counter struct is actually process-local
// common to fe_counters_shared and be_counters_shared
#define COUNTERS_SHARED \
struct { \
uint16_t flags; /* COUNTERS_SHARED_F flags */\
};
#define COUNTERS_SHARED_TG \
struct { \
unsigned long last_change; /* last time, when the state was changed */\
long long srv_aborts; /* aborted responses during DATA phase caused by the server */\
long long cli_aborts; /* aborted responses during DATA phase caused by the client */\
long long internal_errors; /* internal processing errors */\
long long failed_rewrites; /* failed rewrites (warning) */\
long long bytes_out; /* number of bytes transferred from the server to the client */\
long long bytes_in; /* number of bytes transferred from the client to the server */\
long long denied_resp; /* blocked responses because of security concerns */\
long long denied_req; /* blocked requests because of security concerns */\
long long cum_sess; /* cumulated number of accepted connections */\
/* compression counters, index 0 for requests, 1 for responses */\
long long comp_in[2]; /* input bytes fed to the compressor */\
long long comp_out[2]; /* output bytes emitted by the compressor */\
long long comp_byp[2]; /* input bytes that bypassed the compressor (cpu/ram/bw limitation) */\
struct freq_ctr sess_per_sec; /* sessions per second on this server */\
}
// for convenience (generic pointer)
struct counters_shared {
COUNTERS_SHARED;
struct {
COUNTERS_SHARED_TG;
} *tg[MAX_TGROUPS];
};
/* counters used by listeners and frontends */
struct fe_counters {
unsigned int conn_max; /* max # of active sessions */
long long cum_conn; /* cumulated number of received connections */
long long cum_sess; /* cumulated number of accepted connections */
long long cum_sess_ver[3]; /* cumulated number of h1/h2/h3 sessions */
struct fe_counters_shared_tg {
COUNTERS_SHARED_TG;
unsigned int cps_max; /* maximum of new connections received per second */
unsigned int sps_max; /* maximum of new connections accepted per second (sessions) */
long long bytes_in; /* number of bytes transferred from the client to the server */
long long bytes_out; /* number of bytes transferred from the server to the client */
/* compression counters, index 0 for requests, 1 for responses */
long long comp_in[2]; /* input bytes fed to the compressor */
long long comp_out[2]; /* output bytes emitted by the compressor */
long long comp_byp[2]; /* input bytes that bypassed the compressor (cpu/ram/bw limitation) */
long long denied_req; /* blocked requests because of security concerns */
long long denied_resp; /* blocked responses because of security concerns */
long long failed_req; /* failed requests (eg: invalid or timeout) */
long long denied_conn; /* denied connection requests (tcp-req-conn rules) */
long long denied_sess; /* denied session requests (tcp-req-sess rules) */
long long failed_rewrites; /* failed rewrites (warning) */
long long internal_errors; /* internal processing errors */
long long cli_aborts; /* aborted responses during DATA phase caused by the client */
long long srv_aborts; /* aborted responses during DATA phase caused by the server */
long long denied_conn; /* denied connection requests (tcp-req-conn rules) */
long long intercepted_req; /* number of monitoring or stats requests intercepted by the frontend */
long long cum_conn; /* cumulated number of received connections */
struct freq_ctr conn_per_sec; /* received connections per second on the frontend */
struct freq_ctr req_per_sec; /* HTTP requests per second on the frontend */
long long cum_sess_ver[3]; /* cumulated number of h1/h2/h3 sessions */
union {
struct {
long long cum_req[4]; /* cumulated number of processed other/h1/h2/h3 requests */
long long comp_rsp; /* number of compressed responses */
unsigned int rps_max; /* maximum of new HTTP requests second observed */
long long rsp[6]; /* http response codes */
long long cache_lookups;/* cache lookups */
long long cache_hits; /* cache hits */
long long cache_lookups;/* cache lookups */
long long comp_rsp; /* number of compressed responses */
long long rsp[6]; /* http response codes */
} http;
} p; /* protocol-specific stats */
struct freq_ctr sess_per_sec; /* sessions per second on this server */
struct freq_ctr req_per_sec; /* HTTP requests per second on the frontend */
struct freq_ctr conn_per_sec; /* received connections per second on the frontend */
long long failed_req; /* failed requests (eg: invalid or timeout) */
};
unsigned long last_change; /* last time, when the state was changed */
struct fe_counters_shared {
COUNTERS_SHARED;
struct fe_counters_shared_tg *tg[MAX_TGROUPS];
};
struct fe_counters {
struct fe_counters_shared *shared; /* shared counters */
unsigned int conn_max; /* max # of active sessions */
unsigned int cps_max; /* maximum of new connections received per second */
unsigned int sps_max; /* maximum of new connections accepted per second (sessions) */
struct freq_ctr _sess_per_sec; /* sessions per second on this frontend, used to compute sps_max (internal use only) */
struct freq_ctr _conn_per_sec; /* connections per second on this frontend, used to compute cps_max (internal use only) */
union {
struct {
unsigned int rps_max; /* maximum of new HTTP requests second observed */
struct freq_ctr _req_per_sec; /* HTTP requests per second on the frontend, only used to compute rps_max */
} http;
} p; /* protocol-specific stats */
};
struct be_counters_shared_tg {
COUNTERS_SHARED_TG;
long long cum_lbconn; /* cumulated number of sessions processed by load balancing (BE only) */
long long connect; /* number of connection establishment attempts */
long long reuse; /* number of connection reuses */
unsigned long last_sess; /* last session time */
long long failed_checks, failed_hana; /* failed health checks and health analyses for servers */
long long down_trans; /* up->down transitions */
union {
struct {
long long cum_req; /* cumulated number of processed HTTP requests */
long long cache_hits; /* cache hits */
long long cache_lookups;/* cache lookups */
long long comp_rsp; /* number of compressed responses */
long long rsp[6]; /* http response codes */
} http;
} p; /* protocol-specific stats */
long long redispatches; /* retried and redispatched connections (BE only) */
long long retries; /* retried and redispatched connections (BE only) */
long long failed_resp; /* failed responses (BE only) */
long long failed_conns; /* failed connect() attempts (BE only) */
};
struct be_counters_shared {
COUNTERS_SHARED;
struct be_counters_shared_tg *tg[MAX_TGROUPS];
};
/* counters used by servers and backends */
struct be_counters {
struct be_counters_shared *shared; /* shared counters */
unsigned int conn_max; /* max # of active sessions */
long long cum_sess; /* cumulated number of accepted connections */
long long cum_lbconn; /* cumulated number of sessions processed by load balancing (BE only) */
unsigned int cps_max; /* maximum of new connections received per second */
unsigned int sps_max; /* maximum of new connections accepted per second (sessions) */
unsigned int nbpend_max; /* max number of pending connections with no server assigned yet */
unsigned int cur_sess_max; /* max number of currently active sessions */
long long bytes_in; /* number of bytes transferred from the client to the server */
long long bytes_out; /* number of bytes transferred from the server to the client */
/* compression counters, index 0 for requests, 1 for responses */
long long comp_in[2]; /* input bytes fed to the compressor */
long long comp_out[2]; /* output bytes emitted by the compressor */
long long comp_byp[2]; /* input bytes that bypassed the compressor (cpu/ram/bw limitation) */
long long denied_req; /* blocked requests because of security concerns */
long long denied_resp; /* blocked responses because of security concerns */
long long connect; /* number of connection establishment attempts */
long long reuse; /* number of connection reuses */
long long failed_conns; /* failed connect() attempts (BE only) */
long long failed_resp; /* failed responses (BE only) */
long long cli_aborts; /* aborted responses during DATA phase caused by the client */
long long srv_aborts; /* aborted responses during DATA phase caused by the server */
long long retries; /* retried and redispatched connections (BE only) */
long long redispatches; /* retried and redispatched connections (BE only) */
long long failed_rewrites; /* failed rewrites (warning) */
long long internal_errors; /* internal processing errors */
long long failed_checks, failed_hana; /* failed health checks and health analyses for servers */
long long down_trans; /* up->down transitions */
struct freq_ctr _sess_per_sec; /* sessions per second on this frontend, used to compute sps_max (internal use only) */
unsigned int q_time, c_time, d_time, t_time; /* sums of conn_time, queue_time, data_time, total_time */
unsigned int qtime_max, ctime_max, dtime_max, ttime_max; /* maximum of conn_time, queue_time, data_time, total_time observed */
union {
struct {
long long cum_req; /* cumulated number of processed HTTP requests */
long long comp_rsp; /* number of compressed responses */
unsigned int rps_max; /* maximum of new HTTP requests second observed */
long long rsp[6]; /* http response codes */
long long cache_lookups;/* cache lookups */
long long cache_hits; /* cache hits */
} http;
} p; /* protocol-specific stats */
struct freq_ctr sess_per_sec; /* sessions per second on this server */
unsigned long last_sess; /* last session time */
unsigned long last_change; /* last time, when the state was changed */
};
#endif /* _HAPROXY_COUNTERS_T_H */

102
include/haproxy/counters.h Normal file
View File

@ -0,0 +1,102 @@
/*
* include/haproxy/counters.h
* objects counters management
*
* Copyright 2025 HAProxy Technologies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, version 2.1
* exclusively.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_COUNTERS_H
# define _HAPROXY_COUNTERS_H
#include <stddef.h>
#include <haproxy/counters-t.h>
#include <haproxy/guid-t.h>
struct fe_counters_shared *counters_fe_shared_get(const struct guid_node *guid);
struct be_counters_shared *counters_be_shared_get(const struct guid_node *guid);
void counters_fe_shared_drop(struct fe_counters_shared *counters);
void counters_be_shared_drop(struct be_counters_shared *counters);
/* time oriented helper: get last time (relative to current time) on a given
* <scounter> array, for <elem> member (one member per thread group) which is
* assumed to be unsigned long type.
*
* wrapping is handled by taking the lowest diff between now and last counter.
* But since wrapping is expected once every ~136 years (starting 01/01/1970),
* perhaps it's not worth the extra CPU cost.. let's see.
*/
#define COUNTERS_SHARED_LAST_OFFSET(scounters, type, offset) \
({ \
unsigned long last = HA_ATOMIC_LOAD((type *)((char *)scounters[0] + offset));\
unsigned long now_seconds = ns_to_sec(now_ns); \
int it; \
\
for (it = 1; it < global.nbtgroups; it++) { \
unsigned long cur = HA_ATOMIC_LOAD((type *)((char *)scounters[it] + offset));\
if ((now_seconds - cur) < (now_seconds - last)) \
last = cur; \
} \
last; \
})
#define COUNTERS_SHARED_LAST(scounters, elem) \
({ \
int offset = offsetof(typeof(**scounters), elem); \
unsigned long last = COUNTERS_SHARED_LAST_OFFSET(scounters, typeof(scounters[0]->elem), offset); \
\
last; \
})
/* generic unsigned integer addition for all <elem> members from
* <scounters> array (one member per thread group)
* <rfunc> is function taking pointer as parameter to read from the memory
* location pointed to scounters[it].elem
*/
#define COUNTERS_SHARED_TOTAL_OFFSET(scounters, type, offset, rfunc) \
({ \
uint64_t __ret = 0; \
int it; \
\
for (it = 0; it < global.nbtgroups; it++) \
__ret += rfunc((type *)((char *)scounters[it] + offset)); \
__ret; \
})
#define COUNTERS_SHARED_TOTAL(scounters, elem, rfunc) \
({ \
int offset = offsetof(typeof(**scounters), elem); \
uint64_t __ret = COUNTERS_SHARED_TOTAL_OFFSET(scounters, typeof(scounters[0]->elem), offset, rfunc);\
\
__ret; \
})
/* same as COUNTERS_SHARED_TOTAL but with <rfunc> taking 2 extras arguments:
* <arg1> and <arg2>
*/
#define COUNTERS_SHARED_TOTAL_ARG2(scounters, elem, rfunc, arg1, arg2) \
({ \
uint64_t __ret = 0; \
int it; \
\
for (it = 0; it < global.nbtgroups; it++) \
__ret += rfunc(&scounters[it]->elem, arg1, arg2); \
__ret; \
})
#endif /* _HAPROXY_COUNTERS_H */

View File

@ -0,0 +1,68 @@
#ifndef _HAPROXY_CPU_TOPO_T_H
#define _HAPROXY_CPU_TOPO_T_H
#include <haproxy/api-t.h>
#include <haproxy/cpuset-t.h>
/* CPU state flags used with CPU topology detection (ha_cpu_topo.st). We try
* hard to rely on known info. For example we don't claim a CPU is bound or
* online if we don't know, reason why instead we store offline or excluded.
* Other flags like DONT_USE indicate a user's choice while IGNORED indicates
* the result of an automated selection. Two marks are available for allocation
* algorithms to temporarily compare/select/evict CPUs. These must be cleared
* after use.
*/
#define HA_CPU_F_EXCLUDED 0x0001 // this CPU was excluded at boot
#define HA_CPU_F_OFFLINE 0x0002 // this CPU is known to be offline
#define HA_CPU_F_DONT_USE 0x0004 // this CPU must not be used
#define HA_CPU_F_IGNORED 0x0008 // this CPU will not be used
#define HA_CPU_F_EXCL_MASK 0x000F // mask of bits that exclude a CPU
#define HA_CPU_F_MARK1 0x0010 // for temporary internal use only
#define HA_CPU_F_MARK2 0x0020 // for temporary internal use only
#define HA_CPU_F_MARK_MASK 0x0030 // mask to drop the two marks above
/* CPU topology descriptor. All the ID and IDX fields are initialized to -1
* when not known. The identifiers there are mostly assigned on the fly using
* increments and have no particular representation except the fact that CPUs
* having the same ID there share the same designated resource. The flags are
* preset to zero.
*/
struct ha_cpu_topo {
ushort st; // state flags (HA_CPU_F_*)
short idx; // CPU index as passed to the OS. Initially the entry index.
short ca_id[5]; // cache ID for each level (L0 to L4)
short ts_id; // thread-set identifier (generally core number)
short cl_gid; // cluster global identifier (group of more intimate cores)
short cl_lid; // cluster local identifier (per {pkg,node})
short no_id; // NUMA node identifier
short pk_id; // package identifier
short th_cnt; // number of siblings threads
short th_id; // thread ID among siblings of the same core
short capa; // estimated CPU relative capacity; more is better
};
/* Description of a CPU cluster. */
struct ha_cpu_cluster {
uint idx; /* used when sorting, normally the entry index */
uint capa; /* total capacity */
uint nb_cores; /* total distinct cores */
uint nb_cpu; /* total CPUs */
};
/* Description of a CPU selection policy. For now it only associates an option
* name with a callback function that is supposed to adjust the global.nbthread
* and global.nbtgroups based on the policy, the topology, and the constraints
* on the number of threads which must be between tmin and tmax included, and
* the number of thread groups which must be between gmin and gmax included.
* The callback also takes the policy number (cpu_policy) and a pointer to a
* string to write an error to in case of failure (in which case ret must be
* < 0 and the caller will fre the location). More settings might come later.
*/
struct ha_cpu_policy {
const char *name; /* option name in the configuration */
const char *desc; /* short description for help messages */
int (*fct)(int policy, int tmin, int tmax, int gmin, int gmax, char **err);
int arg; /* optional arg for the function */
};
#endif /* _HAPROXY_CPU_TOPO_T_H */

View File

@ -0,0 +1,79 @@
#ifndef _HAPROXY_CPU_TOPO_H
#define _HAPROXY_CPU_TOPO_H
#include <haproxy/api.h>
#include <haproxy/cpuset-t.h>
#include <haproxy/cpu_topo-t.h>
extern int cpu_topo_maxcpus;
extern int cpu_topo_lastcpu;
extern struct ha_cpu_topo *ha_cpu_topo;
/* non-zero if we're certain that taskset or similar was used to force CPUs */
extern int cpu_mask_forced;
/* Detects CPUs that are online on the system. It may rely on FS access (e.g.
* /sys on Linux). Returns the number of CPUs detected or 0 if the detection
* failed.
*/
int ha_cpuset_detect_online(struct hap_cpuset *set);
/* Detects the CPUs that will be used based on the ones the process is bound to.
* Returns non-zero on success, zero on failure. Note that it may not be
* performed in the function above because some calls may rely on other items
* being allocated (e.g. trash).
*/
int cpu_detect_usable(void);
/* detect the CPU topology based on info in /sys */
int cpu_detect_topology(void);
/* fix missing info in the CPU topology */
void cpu_fixup_topology(void);
/* compose clusters */
void cpu_compose_clusters(void);
/* apply remaining topology-based cpu set restrictions */
void cpu_refine_cpusets(void);
/* apply the chosen CPU policy. Returns < 0 on failure with a message in *err
* that must be freed by the caller if non-null.
*/
int cpu_apply_policy(int tmin, int tmax, int gmin, int gmax, char **err);
/* Detects CPUs that are bound to the current process. Returns the number of
* CPUs detected or 0 if the detection failed.
*/
int ha_cpuset_detect_bound(struct hap_cpuset *set);
/* Returns true if at least one cpu-map directive was configured, otherwise
* false.
*/
int cpu_map_configured(void);
/* Dump the CPU topology <topo> for up to cpu_topo_maxcpus CPUs for
* debugging purposes. Offline CPUs are skipped.
*/
void cpu_dump_topology(const struct ha_cpu_topo *topo);
/* re-order a CPU topology array by locality to help form groups. */
void cpu_reorder_by_locality(struct ha_cpu_topo *topo, int entries);
/* re-order a CPU topology array by CPU index only, to undo the function above,
* in case other calls need to be made on top of this.
*/
void cpu_reorder_by_index(struct ha_cpu_topo *topo, int entries);
/* re-order a CPU topology array by cluster id. */
void cpu_reorder_by_cluster(struct ha_cpu_topo *topo, int entries);
/* Functions used by qsort to compare hardware CPUs (not meant to be used from
* outside cpu_topo).
*/
int _cmp_cpu_index(const void *a, const void *b);
int _cmp_cpu_locality(const void *a, const void *b);
int _cmp_cpu_cluster(const void *a, const void *b);
int _cmp_cpu_cluster_capa(const void *a, const void *b);
#endif /* _HAPROXY_CPU_TOPO_H */

View File

@ -44,15 +44,13 @@ int ha_cpuset_ffs(const struct hap_cpuset *set);
*/
void ha_cpuset_assign(struct hap_cpuset *dst, struct hap_cpuset *src);
/* returns true if the sets are equal */
int ha_cpuset_isequal(const struct hap_cpuset *dst, const struct hap_cpuset *src);
/* Returns the biggest index plus one usable on the platform.
*/
int ha_cpuset_size(void);
/* Detects CPUs that are bound to the current process. Returns the number of
* CPUs detected or 0 if the detection failed.
*/
int ha_cpuset_detect_bound(struct hap_cpuset *set);
/* Parse cpu sets. Each CPU set is either a unique number between 0 and
* ha_cpuset_size() - 1 or a range with two such numbers delimited by a dash
* ('-'). Each CPU set can be a list of unique numbers or ranges separated by
@ -62,15 +60,18 @@ int ha_cpuset_detect_bound(struct hap_cpuset *set);
*/
int parse_cpu_set(const char **args, struct hap_cpuset *cpu_set, char **err);
/* Print a cpu-set as compactly as possible and returns the output length.
* Returns >size if it cannot emit anything due to length constraints, in which
* case it will match what is at least needed to go further, and may return 0
* for an empty set. It will emit series of comma-delimited ranges in the form
* "beg[-end]".
*/
int print_cpu_set(char *output, size_t size, const struct hap_cpuset *cpu_set);
/* Parse a linux cpu map string representing to a numeric cpu mask map
* The cpu map string is a list of 4-byte hex strings separated by commas, with
* most-significant byte first, one bit per cpu number.
*/
void parse_cpumap(char *cpumap_str, struct hap_cpuset *cpu_set);
/* Returns true if at least one cpu-map directive was configured, otherwise
* false.
*/
int cpu_map_configured(void);
#endif /* _HAPROXY_CPUSET_H */

View File

@ -28,14 +28,18 @@ extern unsigned int debug_commands_issued;
extern unsigned int warn_blocked_issued;
void ha_task_dump(struct buffer *buf, const struct task *task, const char *pfx);
void ha_thread_dump_one(int thr, int from_signal);
void ha_thread_dump_one(struct buffer *buf, int is_caller);
void ha_dump_backtrace(struct buffer *buf, const char *prefix, int dump);
void ha_backtrace_to_stderr(void);
void ha_panic(void);
void ha_stuck_warning(int thr);
void ha_stuck_warning(void);
void post_mortem_add_component(const char *name, const char *version,
const char *toolchain, const char *toolchain_opts,
const char *build_settings, const char *path);
#ifdef DEBUG_UNIT
void list_unittests();
#endif
#endif /* _HAPROXY_DEBUG_H */

View File

@ -44,7 +44,7 @@
* doesn't engage us too far.
*/
#ifndef MAX_TGROUPS
#define MAX_TGROUPS 16
#define MAX_TGROUPS 32
#endif
#define MAX_THREADS_PER_GROUP __WORDSIZE
@ -53,7 +53,7 @@
* long bits if more tgroups are enabled.
*/
#ifndef MAX_THREADS
#define MAX_THREADS ((((MAX_TGROUPS) > 1) ? 4 : 1) * (MAX_THREADS_PER_GROUP))
#define MAX_THREADS ((((MAX_TGROUPS) > 1) ? 16 : 1) * (MAX_THREADS_PER_GROUP))
#endif
#endif // USE_THREAD
@ -349,6 +349,11 @@
#define SRV_CHK_INTER_THRES 1000
#endif
/* INET6 connectivity caching interval (in ms) */
#ifndef INET6_CONNECTIVITY_CACHE_TIME
#define INET6_CONNECTIVITY_CACHE_TIME 30000
#endif
/* Specifies the string used to report the version and release date on the
* statistics page. May be defined to the empty string ("") to permanently
* disable the feature.
@ -594,6 +599,20 @@
# define DEBUG_STRICT 1
#endif
/* Let's make DEBUG_THREAD default to 1, and make sure it has a value */
#ifndef DEBUG_THREAD
# if defined(USE_THREAD)
# define DEBUG_THREAD 1
# else
# define DEBUG_THREAD 0
# endif
#endif
/* Let's make DEBUG_COUNTERS default to 1 to have glitches counters by default */
#ifndef DEBUG_COUNTERS
# define DEBUG_COUNTERS 1
#endif
#if !defined(DEBUG_MEMORY_POOLS)
# define DEBUG_MEMORY_POOLS 1
#endif
@ -602,4 +621,46 @@
#define MAX_SELF_USE_QUEUE 9
#endif
/*
* FWLC defines
*/
/*
* How many mt_lists we use per tree elements.
* The more lists we have, the less likely it
* will be that we'll have contention when
* inserting/removing an element, but the more
* costly it will be to look up servers.
*/
#ifndef FWLC_LISTS_NB
#define FWLC_LISTS_NB 4
#endif /* FWLC_LISTS_NB */
/*
* How many entries we want to keep in the
* free list, before trying to use some.
* We want to keep some nodes in the tree,
* to avoid having to re-allocate one and
* modify the tree, which requires the
* write lock and is costly, but we
* don't want to have too much, to save
* memory.
*/
#ifndef FWLC_MIN_FREE_ENTRIES
#define FWLC_MIN_FREE_ENTRIES 500
#endif /* FWLC_MIN_FREE_ENTRIES */
/*
* QUIC
*/
/* Memory usage in bytes on Tx side, 0 for unlimited. */
#ifndef QUIC_MAX_TX_MEM
#define QUIC_MAX_TX_MEM 0
#endif
#ifndef STKTABLE_MAX_UPDATES_AT_ONCE
#define STKTABLE_MAX_UPDATES_AT_ONCE 100
#endif /* STKTABLE_MAX_UPDATES_AT_ONCE */
#endif /* _HAPROXY_DEFAULTS_H */

View File

@ -42,6 +42,11 @@
#define DNS_TCP_MSG_MAX_SIZE 65535
#define DNS_TCP_MSG_RING_MAX_SIZE (1 + 1 + 3 + DNS_TCP_MSG_MAX_SIZE) // varint_bytes(DNS_TCP_MSG_MAX_SIZE) == 3
/* threshold to consider that the link to dns server is failing
* and we should stop creating new sessions
*/
#define DNS_MAX_DSS_CONSECUTIVE_ERRORS 100
/* DNS request or response header structure */
struct dns_header {
uint16_t id;
@ -79,7 +84,7 @@ struct dns_additional_record {
struct dns_stream_server {
struct server *srv;
struct dns_ring *ring_req;
int max_slots;
int consecutive_errors; /* number of errors since last successful query (atomically updated without lock) */
int maxconn;
int idle_conns;
int cur_conns;

View File

@ -242,6 +242,7 @@ struct poller {
void (*term)(struct poller *p); /* termination of this poller */
int (*test)(struct poller *p); /* pre-init check of the poller */
int (*fork)(struct poller *p); /* post-fork re-opening */
void (*fixup_tgid_takeover)(struct poller *p, const int fd, const int old_tid, const int old_tgid); /* Fixup anything necessary after a FD takeover across tgids */
const char *name; /* poller name */
unsigned int flags; /* HAP_POLL_F_* */
int pref; /* try pollers with higher preference first */

View File

@ -364,6 +364,20 @@ static inline void fd_lock_tgid(int fd, uint desired_tgid)
}
}
/*
* Try to lock the tgid, keeping the current tgid value.
* Returns 1 on success, or 0 on failure.
*/
static inline int fd_lock_tgid_cur(int fd)
{
uint old = _HA_ATOMIC_LOAD(&fdtab[fd].refc_tgid) & 0x7fff;
if (_HA_ATOMIC_CAS(&fdtab[fd].refc_tgid, &old, (old | 0x8000) + 0x10000))
return 1;
return 0;
}
/* Grab a reference to the FD's TGID, and return the tgid. Note that a TGID of
* zero indicates the FD was closed, thus also fails (i.e. no need to drop it).
* On non-zero (success), the caller must release it using fd_drop_tgid().
@ -373,6 +387,10 @@ static inline uint fd_take_tgid(int fd)
uint old;
old = _HA_ATOMIC_FETCH_ADD(&fdtab[fd].refc_tgid, 0x10000) & 0xffff;
while (old & 0x8000) {
old = _HA_ATOMIC_LOAD(&fdtab[fd].refc_tgid) & 0xffff;
__ha_cpu_relax();
}
if (likely(old))
return old;
HA_ATOMIC_SUB(&fdtab[fd].refc_tgid, 0x10000);
@ -398,6 +416,11 @@ static inline uint fd_grab_tgid(int fd, uint desired_tgid)
uint old;
old = _HA_ATOMIC_FETCH_ADD(&fdtab[fd].refc_tgid, 0x10000) & 0xffff;
/* If the tgid is locked, wait until it no longer is */
while (old & 0x8000) {
old = _HA_ATOMIC_LOAD(&fdtab[fd].refc_tgid) & 0xffff;
__ha_cpu_relax();
}
if (likely(old == desired_tgid))
return 1;
HA_ATOMIC_SUB(&fdtab[fd].refc_tgid, 0x10000);
@ -425,6 +448,22 @@ static inline void fd_claim_tgid(int fd, uint desired_tgid)
}
}
/*
* Update the FD's TGID.
* This should be called with the lock held, and will drop the lock once
* the TGID is updated.
* The reference counter is however preserved.
*/
static inline void fd_update_tgid(int fd, uint desired_tgid)
{
unsigned int orig_tgid = fdtab[fd].refc_tgid;
unsigned int new_tgid;
/* Remove the lock, and switch to the new tgid */
do {
new_tgid = (orig_tgid & 0xffff0000) | desired_tgid;
} while (!_HA_ATOMIC_CAS(&fdtab[fd].refc_tgid, &orig_tgid, new_tgid) && __ha_cpu_relax());
}
/* atomically read the running mask if the tgid matches, or returns zero if it
* does not match. This is meant for use in code paths where the bit is expected
* to be present and will be sufficient to protect against a short-term group
@ -460,6 +499,7 @@ static inline long fd_clr_running(int fd)
static inline void fd_insert(int fd, void *owner, void (*iocb)(int fd), int tgid, unsigned long thread_mask)
{
extern void sock_conn_iocb(int);
struct tgroup_info *tginfo = &ha_tgroup_info[tgid - 1];
int newstate;
/* conn_fd_handler should support edge-triggered FDs */
@ -489,7 +529,7 @@ static inline void fd_insert(int fd, void *owner, void (*iocb)(int fd), int tgid
BUG_ON(fdtab[fd].state != 0);
BUG_ON(tgid < 1 || tgid > MAX_TGROUPS);
thread_mask &= tg->threads_enabled;
thread_mask &= tginfo->threads_enabled;
BUG_ON(thread_mask == 0);
fd_claim_tgid(fd, tgid);

View File

@ -28,7 +28,9 @@
#include <haproxy/ticks.h>
/* exported functions from freq_ctr.c */
ullong _freq_ctr_total_from_values(uint period, int pend, uint tick, ullong past, ullong curr);
ullong freq_ctr_total(const struct freq_ctr *ctr, uint period, int pend);
ullong freq_ctr_total_estimate(const struct freq_ctr *ctr, uint period, int pend);
int freq_ctr_overshoot_period(const struct freq_ctr *ctr, uint period, uint freq);
uint update_freq_ctr_period_slow(struct freq_ctr *ctr, uint period, uint inc);
@ -92,6 +94,16 @@ static inline uint read_freq_ctr_period(const struct freq_ctr *ctr, uint period)
return div64_32(total, period);
}
/* same as read_freq_ctr_period() above except that it doesn't lock and may
* return incorrect values. This is only meant for use in signal handlers.
*/
static inline uint read_freq_ctr_period_estimate(const struct freq_ctr *ctr, uint period)
{
ullong total = freq_ctr_total_estimate(ctr, period, -1);
return div64_32(total, period);
}
/* same as read_freq_ctr_period() above except that floats are used for the
* output so that low rates can be more precise.
*/

View File

@ -79,14 +79,15 @@
#define GTUNE_DISABLE_H2_WEBSOCKET (1<<21)
#define GTUNE_DISABLE_ACTIVE_CLOSE (1<<22)
#define GTUNE_QUICK_EXIT (1<<23)
#define GTUNE_QUIC_SOCK_PER_CONN (1<<24)
/* (1<<24) unused */
#define GTUNE_NO_QUIC (1<<25)
#define GTUNE_USE_FAST_FWD (1<<26)
#define GTUNE_LISTENER_MQ_FAIR (1<<27)
#define GTUNE_LISTENER_MQ_OPT (1<<28)
#define GTUNE_LISTENER_MQ_ANY (GTUNE_LISTENER_MQ_FAIR | GTUNE_LISTENER_MQ_OPT)
#define GTUNE_QUIC_CC_HYSTART (1<<29)
#define GTUNE_QUIC_NO_UDP_GSO (1<<30)
/* subsystem-specific debugging options for tune.debug */
#define GDBG_CPU_AFFINITY (1U<< 0)
#define NO_ZERO_COPY_FWD 0x0001 /* Globally disable zero-copy FF */
#define NO_ZERO_COPY_FWD_PT 0x0002 /* disable zero-copy FF for PT (recv & send are disabled automatically) */
@ -109,6 +110,13 @@ enum {
SSL_SERVER_VERIFY_REQUIRED = 1,
};
/* Takeover across thread groups */
enum threadgroup_takeover {
NO_THREADGROUP_TAKEOVER = 0,
RESTRICTED_THREADGROUP_TAKEOVER = 1,
FULL_THREADGROUP_TAKEOVER = 2,
};
/* bit values to go with "warned" above */
#define WARN_ANY 0x00000001 /* any warning was emitted */
#define WARN_FORCECLOSE_DEPRECATED 0x00000002
@ -161,9 +169,10 @@ struct global {
unsigned char cluster_secret[16]; /* 128 bits of an SHA1 digest of a secret defined as ASCII string */
struct {
int maxpollevents; /* max number of poll events at once */
int max_rules_at_once; /* max number of rules excecuted in a single evaluation loop */
int max_rules_at_once; /* max number of rules executed in a single evaluation loop */
int maxaccept; /* max number of consecutive accept() */
int options; /* various tuning options */
uint debug; /* various debugging options (GDBG_*) */
int runqueue_depth;/* max number of tasks to run at once */
uint recv_enough; /* how many input bytes at once are "enough" */
uint bufsize; /* buffer size in bytes, defaults to BUFSIZE */
@ -175,6 +184,8 @@ struct global {
uint client_rcvbuf; /* set client rcvbuf to this value if not null */
uint server_sndbuf; /* set server sndbuf to this value if not null */
uint server_rcvbuf; /* set server rcvbuf to this value if not null */
uint client_notsent_lowat; /* set client tcp_notsent_lowat to this value if not null */
uint server_notsent_lowat; /* set client tcp_notsent_lowat to this value if not null */
uint frontend_sndbuf; /* set frontend dgram sndbuf to this value if not null */
uint frontend_rcvbuf; /* set frontend dgram rcvbuf to this value if not null */
uint backend_sndbuf; /* set backend dgram sndbuf to this value if not null */
@ -186,6 +197,7 @@ struct global {
int pattern_cache; /* max number of entries in the pattern cache. */
int sslcachesize; /* SSL cache size in session, defaults to 20000 */
int comp_maxlevel; /* max HTTP compression level */
uint glitch_kill_maxidle; /* have glitches kill only below this level of idle */
int pool_low_ratio; /* max ratio of FDs used before we stop using new idle connections */
int pool_high_ratio; /* max ratio of FDs used before we start killing idle connections when creating new connections */
int pool_low_count; /* max number of opened fd before we stop using new idle connections */
@ -199,12 +211,16 @@ struct global {
int default_shards; /* default shards for listeners, or -1 (by-thread) or -2 (by-group) */
uint max_checks_per_thread; /* if >0, no more than this concurrent checks per thread */
uint ring_queues; /* if >0, #ring queues, otherwise equals #thread groups */
enum threadgroup_takeover tg_takeover; /* Policy for threadgroup takeover */
#ifdef USE_QUIC
unsigned int quic_backend_max_idle_timeout;
unsigned int quic_frontend_max_idle_timeout;
unsigned int quic_frontend_glitches_threshold;
unsigned int quic_frontend_max_data;
unsigned int quic_frontend_max_streams_bidi;
uint64_t quic_frontend_max_tx_mem;
size_t quic_frontend_max_window_size;
unsigned int quic_frontend_stream_data_ratio;
unsigned int quic_retry_threshold;
unsigned int quic_reorder_ratio;
unsigned int quic_max_frame_loss;

View File

@ -158,7 +158,6 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
struct http_hdr *hdr, unsigned int hdr_num,
struct h1m *h1m, union h1_sl *slp);
int h1_parse_cont_len_header(struct h1m *h1m, struct ist *value);
int h1_parse_xfer_enc_header(struct h1m *h1m, struct ist value);
void h1_parse_connection_header(struct h1m *h1m, struct ist *value);
void h1_parse_upgrade_header(struct h1m *h1m, struct ist value);

View File

@ -38,7 +38,7 @@ int h1_parse_msg_tlrs(struct h1m *h1m, struct htx *dsthtx,
struct buffer *srcbuf, size_t ofs, size_t max);
/* Returns the URI of an HTX message in the most common format for a H1 peer. It
* is the path part of an absolute URI when the URI was normalized, ortherwise
* is the path part of an absolute URI when the URI was normalized, otherwise
* it is the whole URI, as received. Concretely, it is only a special case for
* URIs received from H2 clients, to be able to send a relative path the H1
* servers.

View File

@ -60,6 +60,7 @@
#define CLASS_CERTCACHE "CertCache"
#define CLASS_PROXY_LIST "ProxyList"
#define CLASS_SERVER_LIST "ServerList"
#define CLASS_QUEUE "Queue"
struct stream;

View File

@ -50,6 +50,7 @@
#define HLUA_INIT(__hlua) do { (__hlua)->T = 0; } while(0)
/* Lua HAProxy integration functions. */
void hlua_yield_asap(lua_State *L);
const char *hlua_traceback(lua_State *L, const char* sep);
void hlua_ctx_destroy(struct hlua *lua);
void hlua_init();

View File

@ -232,6 +232,52 @@ static inline int http_path_has_forbidden_char(const struct ist ist, const char
return 0;
}
/* Checks whether the :authority pseudo header contains dangerous chars that
* might affect its reassembly. We want to catch anything below 0x21, above
* 0x7e, as well as '@', '[', ']', '/','?', '#', '\', CR, LF, NUL. Then we
* fall back to the slow path and decide. Brackets are used for IP-literal and
* deserve special case, that is better handled in the slow path. The function
* returns 0 if no forbidden char is presnet, non-zero otherwise.
*/
static inline int http_authority_has_forbidden_char(const struct ist ist)
{
size_t ofs, len = istlen(ist);
const char *p = istptr(ist);
int brackets = 0;
uchar c;
/* Many attempts with various methods have shown that moderately recent
* compilers (gcc >= 9, clang >= 13) will arrange the code below as an
* evaluation tree that remains efficient at -O2 and above (~1.2ns per
* char). The immediate next efficient one is the bitmap from 64-bit
* registers but it's extremely sensitive to code arrangements and
* optimization.
*/
for (ofs = 0; ofs < len; ofs++) {
c = p[ofs];
if (unlikely(c < 0x21 || c > 0x7e ||
c == '#' || c == '/' || c == '?' || c == '@' ||
c == '[' || c == '\\' || c == ']')) {
/* all of them must be rejected, except '[' which may
* only appear at the beginning, and ']' which may
* only appear at the end or before a colon.
*/
if ((c == '[' && ofs == 0) ||
(c == ']' && (ofs == len - 1 || p[ofs + 1] == ':'))) {
/* that's an IP-literal (see RFC3986#3.2), it's
* OK for now.
*/
brackets ^= 1;
} else {
return 1;
}
}
}
/* there must be no opening bracket left nor lone closing one */
return brackets;
}
/* Checks status code array <array> for the presence of status code <status>.
* Returns non-zero if the code is present, zero otherwise. Any status code is
* permitted.

View File

@ -168,7 +168,7 @@ enum {
REDIRECT_FLAG_FROM_REQ = 4, /* redirect rule on the request path */
REDIRECT_FLAG_IGNORE_EMPTY = 8, /* silently ignore empty location expressions */
REDIRECT_FLAG_KEEP_QS = 16, /* append the query string to location, if any */
REDIRECT_FLAG_COOKIE_FMT = 32, /* The cookie value is a log-format stirng*/
REDIRECT_FLAG_COOKIE_FMT = 32, /* The cookie value is a log-format string */
};
/* Redirect types (location, prefix, extended ) */

View File

@ -61,4 +61,10 @@ struct per_thread_deinit_fct {
void (*fct)();
};
struct unittest_fct {
struct list list;
const char *name;
int (*fct)(int argc, char **argv);
};
#endif /* _HAPROXY_INIT_T_H */

View File

@ -32,6 +32,17 @@ void hap_register_per_thread_init(int (*fct)());
void hap_register_per_thread_deinit(void (*fct)());
void hap_register_per_thread_free(void (*fct)());
#ifdef DEBUG_UNIT
void hap_register_unittest(const char *name, int (*fct)(int, char **));
/* Simplified way to register a unit test */
#define REGISTER_UNITTEST(name, fct) \
INITCALL2(STG_REGISTER, hap_register_unittest, name, (fct))
#else
#define hap_register_unittest(a,b) ({})
#define REGISTER_UNITTEST(name, fct)
#endif
/* simplified way to declare a pre-check callback in a file */
#define REGISTER_PRE_CHECK(fct) \
INITCALL1(STG_REGISTER, hap_register_pre_check, (fct))

18
include/haproxy/jws.h Normal file
View File

@ -0,0 +1,18 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#ifndef _HAPROXY_JWK_H_
#define _HAPROXY_JWK_H_
#include <haproxy/openssl-compat.h>
#include <haproxy/jwt-t.h>
int bn2base64url(const BIGNUM *bn, char *dst, size_t dsize);
int EVP_PKEY_to_pub_jwk(EVP_PKEY *pkey, char *dst, size_t dsize);
enum jwt_alg EVP_PKEY_to_jws_alg(EVP_PKEY *pkey);
int jws_b64_payload(char *payload, char *dst, size_t dsize);
int jws_b64_protected(enum jwt_alg alg, char *kid, char *jwk, char *nonce, char *url, char *dst, size_t dsize);
int jws_b64_signature(EVP_PKEY *pkey, enum jwt_alg alg, char *b64protected, char *b64payload, char *dst, size_t dsize);
int jws_flattened(char *protected, char *payload, char *signature, char *dst, size_t dsize);
int jws_thumbprint(EVP_PKEY *pkey, char *dst, size_t dsize);
#endif /* ! _HAPROXY_JWK_H_ */

View File

@ -23,6 +23,7 @@
#define _HAPROXY_LB_FWRR_T_H
#include <import/ebtree-t.h>
#include <haproxy/thread-t.h>
/* This structure is used to apply fast weighted round robin on a server group */
struct fwrr_group {
@ -32,12 +33,17 @@ struct fwrr_group {
struct eb_root *next; /* servers to be placed at next run */
int curr_pos; /* current position in the tree */
int curr_weight; /* total weight of the current time range */
int next_weight; /* total weight of the next time range */
};
struct lb_fwrr_per_tgrp {
struct fwrr_group act; /* weighted round robin on the active servers */
struct fwrr_group bck; /* weighted round robin on the backup servers */
__decl_thread(HA_RWLOCK_T lock);
};
struct lb_fwrr {
struct fwrr_group act; /* weighted round robin on the active servers */
struct fwrr_group bck; /* weighted round robin on the backup servers */
int next_weight_act; /* total weight of the next time range on active servers, for all trees */
int next_weight_bck; /* total weight of the next time range on backup servers, for all trees */
};
#endif /* _HAPROXY_LB_FWRR_T_H */

View File

@ -121,6 +121,7 @@ enum li_status {
#define BC_SSL_O_NONE 0x0000
#define BC_SSL_O_NO_TLS_TICKETS 0x0100 /* disable session resumption tickets */
#define BC_SSL_O_PREF_CLIE_CIPH 0x0200 /* prefer client ciphers */
#define BC_SSL_O_STRICT_SNI 0x0400 /* refuse negotiation if sni doesn't match a certificate */
#endif
struct tls_version_filter {
@ -169,7 +170,6 @@ struct bind_conf {
unsigned long long ca_ignerr_bitfield[IGNERR_BF_SIZE]; /* ignored verify errors in handshake if depth > 0 */
unsigned long long crt_ignerr_bitfield[IGNERR_BF_SIZE]; /* ignored verify errors in handshake if depth == 0 */
void *initial_ctx; /* SSL context for initial negotiation */
int strict_sni; /* refuse negotiation if sni doesn't match a certificate */
int ssl_options; /* ssl options */
struct eb_root sni_ctx; /* sni_ctx tree of all known certs full-names sorted by name */
struct eb_root sni_w_ctx; /* sni_ctx tree of all known certs wildcards sorted by name */
@ -193,6 +193,7 @@ struct bind_conf {
unsigned int analysers; /* bitmap of required protocol analysers */
int maxseg; /* for TCP, advertised MSS */
int tcp_ut; /* for TCP, user timeout */
int idle_ping; /* MUX idle-ping interval in ms */
int maxaccept; /* if set, max number of connections accepted at once (-1 when disabled) */
unsigned int backlog; /* if set, listen backlog */
int maxconn; /* maximum connections allowed on this listener */
@ -243,7 +244,7 @@ struct listener {
struct fe_counters *counters; /* statistics counters */
struct mt_list wait_queue; /* link element to make the listener wait for something (LI_LIMITED) */
char *name; /* listener's name */
char *label; /* listener's label */
unsigned int thr_conn[MAX_THREADS_PER_GROUP]; /* number of connections per thread for the group */
struct list by_fe; /* chaining in frontend's list of listeners */

View File

@ -36,6 +36,7 @@ extern struct pool_head *pool_head_uniqueid;
extern const char *log_levels[];
extern char *log_format;
extern char httpclient_log_format[];
extern char httpsclient_log_format[];
extern char default_tcp_log_format[];
extern char clf_tcp_log_format[];
extern char default_http_log_format[];
@ -144,7 +145,7 @@ void app_log(struct list *loggers, struct buffer *tag, int level, const char *fo
*/
int add_to_logformat_list(char *start, char *end, int type, struct lf_expr *lf_expr, char **err);
ssize_t syslog_applet_append_event(void *ctx, struct ist v1, struct ist v2, size_t ofs, size_t len);
ssize_t syslog_applet_append_event(void *ctx, struct ist v1, struct ist v2, size_t ofs, size_t len, char delim);
/*
* Parse the log_format string and fill a linked list.

View File

@ -70,6 +70,9 @@
#define H2_CF_ERROR 0x01000000 //A read error was detected (handled has an abort)
#define H2_CF_WAIT_INLIST 0x02000000 // there is at least one stream blocked by another stream in send_list/fctl_list
#define H2_CF_IDL_PING 0x04000000 // timer task scheduled for a PING emission
#define H2_CF_IDL_PING_SENT 0x08000000 // PING emitted, or will be on next tasklet run, waiting for ACK
/* This function is used to report flags in debugging tools. Please reflect
* below any single-bit flag addition above in the same order via the
* __APPEND_FLAG macro. The new end of the buffer is returned.

View File

@ -17,6 +17,7 @@
#include <haproxy/quic_frame-t.h>
#include <haproxy/quic_pacing-t.h>
#include <haproxy/quic_stream-t.h>
#include <haproxy/quic_utils-t.h>
#include <haproxy/stconn-t.h>
#include <haproxy/task-t.h>
#include <haproxy/time-t.h>
@ -27,16 +28,20 @@ enum qcs_type {
QCS_SRV_BIDI,
QCS_CLT_UNI,
QCS_SRV_UNI,
/* Must be the last one */
QCS_MAX_TYPES
};
enum qcc_app_st {
QCC_APP_ST_NULL,
QCC_APP_ST_INIT,
QCC_APP_ST_SHUT,
} __attribute__((packed));
struct qcc {
struct connection *conn;
uint64_t nb_sc; /* number of attached stream connectors */
uint64_t nb_hreq; /* number of in-progress http requests */
uint32_t flags; /* QC_CF_* */
enum qcc_app_st app_st; /* application layer state */
int glitches; /* total number of glitches on this connection */
/* flow-control fields set by us enforced on our side. */
@ -105,9 +110,6 @@ struct qcc {
void *ctx; /* Application layer context */
};
/* Maximum size of stream Rx buffer. */
#define QC_S_RX_BUF_SZ (global.tune.bufsize - NCB_RESERVED_SZ)
/* QUIC stream states
*
* On initialization a stream is put on idle state. It is opened as soon as
@ -130,6 +132,15 @@ enum qcs_state {
QC_SS_CLO, /* closed */
} __attribute__((packed));
/* STREAM receive buffer. Can handle out-of-order storage.
* Can be used as a tree node to allocate multiple entries ordered by offsets.
*/
struct qc_stream_rxbuf {
struct eb64_node off_node; /* base offset of current buffer, node for QCS rx.bufs */
struct ncbuf ncb; /* data storage with support for out of order offset */
uint64_t off_end; /* first offset directly outside of current buffer */
};
struct qcs {
struct qcc *qcc;
struct sedesc *sd;
@ -138,15 +149,17 @@ struct qcs {
void *ctx; /* app-ops context */
struct {
uint64_t offset; /* absolute current base offset of ncbuf */
uint64_t offset; /* read offset */
uint64_t offset_max; /* maximum absolute offset received */
struct ncbuf ncbuf; /* receive buffer - can handle out-of-order offset frames */
struct eb_root bufs; /* receive buffers tree ordered by offset */
struct buffer app_buf; /* receive buffer used by stconn layer */
uint64_t msd; /* current max-stream-data limit to enforce */
uint64_t msd_init; /* initial max-stream-data */
uint64_t msd_base; /* max-stream-data previous to latest update */
struct bdata_ctr data; /* data utilization counter. Note that <tot> is now used for now as accounting may be difficult with ncbuf. */
} rx;
struct {
struct quic_fctl fc; /* stream flow control applied on sending */
struct quic_frame *msd_frm; /* MAX_STREAM_DATA frame prepared */
} tx;
struct eb64_node by_id;
@ -189,7 +202,7 @@ struct qcc_app_ops {
/* Initialize <qcs> stream app context or leave it to NULL if rejected. */
int (*attach)(struct qcs *qcs, void *conn_ctx);
/* Convert received HTTP payload to HTX. */
/* Convert received HTTP payload to HTX. Returns amount of decoded bytes from <b> or a negative error code. */
ssize_t (*rcv_buf)(struct qcs *qcs, struct buffer *b, int fin);
/* Convert HTX to HTTP payload for sending. */
@ -219,9 +232,9 @@ struct qcc_app_ops {
#define QC_CF_ERRL 0x00000001 /* fatal error detected locally, connection should be closed soon */
#define QC_CF_ERRL_DONE 0x00000002 /* local error properly handled, connection can be released */
/* unused 0x00000004 */
#define QC_CF_IS_BACK 0x00000004 /* backend side */
#define QC_CF_CONN_FULL 0x00000008 /* no stream buffers available on connection */
#define QC_CF_APP_SHUT 0x00000010 /* Application layer shutdown done. */
/* unused 0x00000010 */
#define QC_CF_ERR_CONN 0x00000020 /* fatal error reported by transport layer */
#define QC_CF_WAIT_HS 0x00000040 /* MUX init before QUIC handshake completed (0-RTT) */
@ -238,9 +251,8 @@ static forceinline char *qcc_show_flags(char *buf, size_t len, const char *delim
_(QC_CF_ERRL,
_(QC_CF_ERRL_DONE,
_(QC_CF_CONN_FULL,
_(QC_CF_APP_SHUT,
_(QC_CF_ERR_CONN,
_(QC_CF_WAIT_HS))))));
_(QC_CF_WAIT_HS)))));
/* epilogue */
_(~0U);
return buf;

View File

@ -46,15 +46,12 @@ int qcc_recv_max_stream_data(struct qcc *qcc, uint64_t id, uint64_t max);
int qcc_recv_reset_stream(struct qcc *qcc, uint64_t id, uint64_t err, uint64_t final_size);
int qcc_recv_stop_sending(struct qcc *qcc, uint64_t id, uint64_t err);
/* Bit shift to get the stream sub ID for internal use which is obtained
* shifting the stream IDs by this value, knowing that the
* QCS_ID_TYPE_SHIFT less significant bits identify the stream ID
* types (client initiated bidirectional, server initiated bidirectional,
* client initiated unidirectional, server initiated bidirectional).
* Note that there is no reference to such stream sub IDs in the RFC.
*/
static inline int qmux_stream_rx_bufsz(void)
{
return global.tune.bufsize - NCB_RESERVED_SZ;
}
#define QCS_ID_TYPE_MASK 0x3
#define QCS_ID_TYPE_SHIFT 2
/* The less significant bit of a stream ID is set for a server initiated stream */
#define QCS_ID_SRV_INTIATOR_BIT 0x1
/* This bit is set for unidirectional streams */
@ -77,16 +74,6 @@ static inline int quic_stream_is_remote(struct qcc *qcc, uint64_t id)
return !quic_stream_is_local(qcc, id);
}
static inline int quic_stream_is_uni(uint64_t id)
{
return id & QCS_ID_DIR_BIT;
}
static inline int quic_stream_is_bidi(uint64_t id)
{
return !quic_stream_is_uni(id);
}
static inline char *qcs_st_to_str(enum qcs_state st)
{
switch (st) {

View File

@ -87,10 +87,9 @@ static forceinline char *spop_strm_show_flags(char *buf, size_t len, const char
/* SPOP connection state (spop_conn->state) */
enum spop_conn_st {
SPOP_CS_HA_HELLO = 0, /* init done, waiting for sending HELLO frame */
SPOP_CS_AGENT_HELLO, /* HELLO frame sent, waiting for agent HELLO frame to define the connection settings */
SPOP_CS_FRAME_H, /* HELLO handshake finished, waiting for a frame header */
SPOP_CS_FRAME_P, /* Frame header received, waiting for a frame data */
SPOP_CS_HA_HELLO = 0, /* init done, waiting for sending HELLO frame */
SPOP_CS_AGENT_HELLO, /* HELLO frame sent, waiting for agent HELLO frame to define the connection settings */
SPOP_CS_RUNNING, /* HELLO handshake finished, exchange NOTIFY/ACK frames */
SPOP_CS_ERROR, /* send DISCONNECT frame to be able ti close the connection */
SPOP_CS_CLOSING, /* DISCONNECT frame sent, waiting for the agent DISCONNECT frame before closing */
SPOP_CS_CLOSED, /* Agent DISCONNECT frame received and close the connection ASAP */
@ -103,8 +102,7 @@ static inline const char *spop_conn_st_to_str(enum spop_conn_st st)
switch (st) {
case SPOP_CS_HA_HELLO : return "HHL";
case SPOP_CS_AGENT_HELLO: return "AHL";
case SPOP_CS_FRAME_H : return "FRH";
case SPOP_CS_FRAME_P : return "FRP";
case SPOP_CS_RUNNING : return "RUN";
case SPOP_CS_ERROR : return "ERR";
case SPOP_CS_CLOSING : return "CLI";
case SPOP_CS_CLOSED : return "CLO";

View File

@ -46,7 +46,25 @@
#ifdef USE_QUIC_OPENSSL_COMPAT
#include <haproxy/quic_openssl_compat.h>
#else
#if defined(OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_SEND)
/* This macro is defined by the new OpenSSL 3.5.0 QUIC TLS API and it is not
* defined by quictls.
*/
#define HAVE_OPENSSL_QUIC
#define SSL_set_quic_transport_params SSL_set_quic_tls_transport_params
#define SSL_set_quic_early_data_enabled SSL_set_quic_tls_early_data_enabled
#define SSL_quic_read_level(arg) -1
enum ssl_encryption_level_t {
ssl_encryption_initial = 0,
ssl_encryption_early_data,
ssl_encryption_handshake,
ssl_encryption_application
};
#endif
#endif /* USE_QUIC_OPENSSL_COMPAT */
#if defined(OPENSSL_IS_AWSLC)
#define OPENSSL_NO_DH
@ -128,6 +146,14 @@
#define HAVE_SSL_SET_SECURITY_LEVEL
#endif
#if ((defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x3030600L)) || (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L) || defined(OPENSSL_IS_AWSLC)) && !defined(USE_OPENSSL_WOLFSSL)
#define HAVE_JWS
#endif
#if (defined(HAVE_JWS) && defined(HAVE_ASN1_TIME_TO_TM))
#define HAVE_ACME
#endif
#if !defined(HAVE_SSL_SET_SECURITY_LEVEL)
/* define a nope function for set_security_level */
#define SSL_CTX_set_security_level(ctx, level) ({})
@ -404,7 +430,7 @@ static inline unsigned long ERR_peek_error_func(const char **func)
#define SSL_OP_CIPHER_SERVER_PREFERENCE 0
#endif
/* needs OpenSSL >= 0.9.7 and renegotation options on WolfSSL */
/* needs OpenSSL >= 0.9.7 and renegotiation options on WolfSSL */
#if !defined(SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION) || \
(defined(USE_OPENSSL_WOLFSSL) && !defined(HAVE_SECURE_RENEGOTIATION) && !defined(HAVE_SERVER_RENEGOTIATION_INFO))
#undef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION

View File

@ -38,7 +38,7 @@ extern int (*const pat_index_fcts[PAT_MATCH_NUM])(struct pattern_expr *, struct
extern void (*const pat_prune_fcts[PAT_MATCH_NUM])(struct pattern_expr *);
extern struct pattern *(*const pat_match_fcts[PAT_MATCH_NUM])(struct sample *, struct pattern_expr *, int);
/* This is the root of the list of all pattern_ref avalaibles. */
/* This is the root of the list of all available pattern_ref values. */
extern struct list pattern_reference;
int pattern_finalize_config(void);

View File

@ -27,6 +27,7 @@
#define MEM_F_SHARED 0x1
#define MEM_F_EXACT 0x2
#define MEM_F_UAF 0x4
/* A special pointer for the pool's free_list that indicates someone is
* currently manipulating it. Serves as a short-lived lock.
@ -51,6 +52,7 @@
#define POOL_DBG_TAG 0x00000080 // place a tag at the end of the area
#define POOL_DBG_POISON 0x00000100 // poison memory area on pool_alloc()
#define POOL_DBG_UAF 0x00000200 // enable use-after-free protection
#define POOL_DBG_BACKUP 0x00000400 // backup the object contents on free()
/* This is the head of a thread-local cache */
@ -62,6 +64,17 @@ struct pool_cache_head {
ulong fill_pattern; /* pattern used to fill the area on free */
} THREAD_ALIGNED(64);
/* This describes a pool registration, which is what was passed to
* create_pool() and that might have been merged with an existing pool.
*/
struct pool_registration {
struct list list; /* link element */
char name[12]; /* name of the pool */
unsigned int size; /* expected object size */
unsigned int flags; /* MEM_F_* */
unsigned int align; /* expected alignment; 0=unspecified */
};
/* This represents one item stored in the thread-local cache. <by_pool> links
* the object to the list of objects in the pool, and <by_lru> links the object
* to the local thread's list of hottest objects. This way it's possible to
@ -114,9 +127,11 @@ struct pool_head {
unsigned int flags; /* MEM_F_* */
unsigned int users; /* number of pools sharing this zone */
unsigned int alloc_sz; /* allocated size (includes hidden fields) */
unsigned int sum_size; /* sum of all registered users' size */
struct list list; /* list of all known pools */
void *base_addr; /* allocation address, for free() */
char name[12]; /* name of the pool */
struct list regs; /* registrations: alt names for this pool */
/* heavily read-write part */
THREAD_ALIGN(64);

View File

@ -30,4 +30,6 @@ extern struct protocol proto_quic6;
extern struct quic_dghdlr *quic_dghdlrs;
extern THREAD_LOCAL struct cshared quic_mem_diff;
#endif /* _HAPROXY_PROTO_QUIC_H */

View File

@ -45,15 +45,19 @@
#include <haproxy/uri_auth-t.h>
#include <haproxy/http_ext-t.h>
/* values for proxy->mode */
/* values for proxy->mode, only one value per proxy.
*
* values are bitfield compatible so that functions may
* take a bitfield of compatible modes as parameter
*/
enum pr_mode {
PR_MODE_TCP = 0,
PR_MODE_HTTP,
PR_MODE_CLI,
PR_MODE_SYSLOG,
PR_MODE_PEERS,
PR_MODE_SPOP,
PR_MODES
PR_MODES = 0x00,
PR_MODE_TCP = 0x01,
PR_MODE_HTTP = 0x02,
PR_MODE_CLI = 0x04,
PR_MODE_SYSLOG = 0x08,
PR_MODE_PEERS = 0x10,
PR_MODE_SPOP = 0x20,
} __attribute__((packed));
enum PR_SRV_STATE_FILE {
@ -110,11 +114,12 @@ enum PR_SRV_STATE_FILE {
#define PR_O_HTTP_CLO 0x01000000 /* HTTP close mode (httpclose) */
#define PR_O_HTTP_SCL 0x02000000 /* HTTP server close mode (http-server-close) */
#define PR_O_HTTP_MODE 0x03000000 /* MASK to retrieve the HTTP mode */
/* unused: 0x04000000 */
#define PR_O_TCPCHK_SSL 0x08000000 /* at least one TCPCHECK connect rule requires SSL */
#define PR_O_CONTSTATS 0x10000000 /* continuous counters */
/* unused: 0x20000000 */
#define PR_O_HTTP_DROP_REQ_TRLS 0x04000000 /* Drop the request trailers when forwarding to the server */
#define PR_O_HTTP_DROP_RES_TRLS 0x08000000 /* Drop response trailers when forwarding to the client */
#define PR_O_TCPCHK_SSL 0x10000000 /* at least one TCPCHECK connect rule requires SSL */
#define PR_O_CONTSTATS 0x20000000 /* continuous counters */
#define PR_O_DISABLE404 0x40000000 /* Disable a server on a 404 response to a health-check */
/* unused: 0x80000000 */
@ -151,7 +156,8 @@ enum PR_SRV_STATE_FILE {
#define PR_O2_RSTRICT_REQ_HDR_NAMES_DEL 0x00800000 /* remove request header names containing chars outside of [0-9a-zA-Z-] charset */
#define PR_O2_RSTRICT_REQ_HDR_NAMES_NOOP 0x01000000 /* preserve request header names containing chars outside of [0-9a-zA-Z-] charset */
#define PR_O2_RSTRICT_REQ_HDR_NAMES_MASK 0x01c00000 /* mask for restrict-http-header-names option */
/* unused : 0x0000000..0x80000000 */
/* unused : 0x02000000 ... 0x08000000 */
/* server health checks */
#define PR_O2_CHK_NONE 0x00000000 /* no L7 health checks configured (TCP by default) */
@ -161,6 +167,29 @@ enum PR_SRV_STATE_FILE {
#define PR_O2_CHK_ANY 0xF0000000 /* Mask to cover any check */
/* end of proxy->options2 */
/* bits for proxy->options3 */
/* bits for log-forward proxies */
#define PR_O3_DONTPARSELOG 0x00000001 /* don't parse log messages */
#define PR_O3_ASSUME_RFC6587_NTF 0x00000002 /* assume that we are going to receive just non-transparent framing messages */
/* unused: 0x00000004 to 0x00000008 */
#define PR_O3_LOGF_HOST_REPLACE 0x00000010
#define PR_O3_LOGF_HOST_FILL 0x00000020
#define PR_O3_LOGF_HOST_KEEP 0x00000040
#define PR_O3_LOGF_HOST_APPEND 0x00000080
#define PR_O3_LOGF_HOST 0x000000F0
/* bits for hash-preserve-affinity */
#define PR_O3_HASHAFNTY_ALWS 0x00000000 /* always preserve hash affinity */
#define PR_O3_HASHAFNTY_MAXCONN 0x00000100 /* preserve hash affinity until maxconn is reached */
#define PR_O3_HASHAFNTY_MAXQUEUE 0x00000200 /* preserve hash affinity until maxqueue is reached */
#define PR_O3_HASHAFNTY_MASK 0x00000300 /* mask for hash-preserve-affinity */
/* unused: 0x00000400 to 0x80000000 */
/* end of proxy->options3 */
/* Cookie settings for pr->ck_opts */
#define PR_CK_RW 0x00000001 /* rewrite all direct cookies with the right serverid */
#define PR_CK_IND 0x00000002 /* keep only indirect cookies */
@ -271,9 +300,10 @@ struct error_snapshot {
char buf[VAR_ARRAY]; /* copy of the beginning of the message for bufsize bytes */
};
/* Each proxy will have one occurence of this structure per thread group */
/* Each proxy will have one occurrence of this structure per thread group */
struct proxy_per_tgroup {
struct queue queue;
struct lbprm_per_tgrp lbprm;
} THREAD_ALIGNED(64);
struct proxy {
@ -281,10 +311,15 @@ struct proxy {
char flags; /* bit field PR_FL_* */
enum pr_mode mode; /* mode = PR_MODE_TCP, PR_MODE_HTTP, ... */
char cap; /* supported capabilities (PR_CAP_*) */
/* 4-bytes hole */
struct list global_list; /* list member for global proxy list */
unsigned int maxconn; /* max # of active streams on the frontend */
int options; /* PR_O_REDISP, PR_O_TRANSP, ... */
int options2; /* PR_O2_* */
int options3; /* PR_O3_* */
unsigned int ck_opts; /* PR_CK_* (cookie options) */
unsigned int fe_req_ana, be_req_ana; /* bitmap of common request protocol analysers for the frontend and backend */
unsigned int fe_rsp_ana, be_rsp_ana; /* bitmap of common response protocol analysers for the frontend and backend */
@ -420,6 +455,7 @@ struct proxy {
/* used only during configuration parsing */
int no_options; /* PR_O_REDISP, PR_O_TRANSP, ... */
int no_options2; /* PR_O2_* */
int no_options3; /* PR_O3_* */
struct {
const char *file; /* file where the section appears */

View File

@ -33,12 +33,14 @@
#include <haproxy/thread.h>
extern struct proxy *proxies_list;
extern struct list proxies;
extern struct eb_root used_proxy_id; /* list of proxy IDs in use */
extern unsigned int error_snapshot_id; /* global ID assigned to each error then incremented */
extern struct eb_root proxy_by_name; /* tree of proxies sorted by name */
extern const struct cfg_opt cfg_opts[];
extern const struct cfg_opt cfg_opts2[];
extern const struct cfg_opt cfg_opts3[];
struct task *manage_proxy(struct task *t, void *context, unsigned int state);
void proxy_cond_pause(struct proxy *p);
@ -50,6 +52,7 @@ int resume_proxy(struct proxy *p);
void stop_proxy(struct proxy *p);
int stream_set_backend(struct stream *s, struct proxy *be);
void deinit_proxy(struct proxy *p);
void free_proxy(struct proxy *p);
const char *proxy_cap_str(int cap);
const char *proxy_mode_str(int mode);
@ -69,6 +72,7 @@ void proxy_destroy_all_unref_defaults(void);
void proxy_ref_defaults(struct proxy *px, struct proxy *defpx);
void proxy_unref_defaults(struct proxy *px);
void proxy_unref_or_destroy_defaults(struct proxy *px);
int setup_new_proxy(struct proxy *px, const char *name, unsigned int cap, char **errmsg);
struct proxy *alloc_new_proxy(const char *name, unsigned int cap,
char **errmsg);
struct proxy *parse_new_proxy(const char *name, unsigned int cap,
@ -132,22 +136,24 @@ static inline void proxy_reset_timeouts(struct proxy *proxy)
/* increase the number of cumulated connections received on the designated frontend */
static inline void proxy_inc_fe_conn_ctr(struct listener *l, struct proxy *fe)
{
_HA_ATOMIC_INC(&fe->fe_counters.cum_conn);
_HA_ATOMIC_INC(&fe->fe_counters.shared->tg[tgid - 1]->cum_conn);
if (l && l->counters)
_HA_ATOMIC_INC(&l->counters->cum_conn);
_HA_ATOMIC_INC(&l->counters->shared->tg[tgid - 1]->cum_conn);
update_freq_ctr(&fe->fe_counters.shared->tg[tgid - 1]->conn_per_sec, 1);
HA_ATOMIC_UPDATE_MAX(&fe->fe_counters.cps_max,
update_freq_ctr(&fe->fe_counters.conn_per_sec, 1));
update_freq_ctr(&fe->fe_counters._conn_per_sec, 1));
}
/* increase the number of cumulated connections accepted by the designated frontend */
static inline void proxy_inc_fe_sess_ctr(struct listener *l, struct proxy *fe)
{
_HA_ATOMIC_INC(&fe->fe_counters.cum_sess);
_HA_ATOMIC_INC(&fe->fe_counters.shared->tg[tgid - 1]->cum_sess);
if (l && l->counters)
_HA_ATOMIC_INC(&l->counters->cum_sess);
_HA_ATOMIC_INC(&l->counters->shared->tg[tgid - 1]->cum_sess);
update_freq_ctr(&fe->fe_counters.shared->tg[tgid - 1]->sess_per_sec, 1);
HA_ATOMIC_UPDATE_MAX(&fe->fe_counters.sps_max,
update_freq_ctr(&fe->fe_counters.sess_per_sec, 1));
update_freq_ctr(&fe->fe_counters._sess_per_sec, 1));
}
/* increase the number of cumulated HTTP sessions on the designated frontend.
@ -157,20 +163,21 @@ static inline void proxy_inc_fe_cum_sess_ver_ctr(struct listener *l, struct prox
unsigned int http_ver)
{
if (http_ver == 0 ||
http_ver > sizeof(fe->fe_counters.cum_sess_ver) / sizeof(*fe->fe_counters.cum_sess_ver))
http_ver > sizeof(fe->fe_counters.shared->tg[tgid - 1]->cum_sess_ver) / sizeof(*fe->fe_counters.shared->tg[tgid - 1]->cum_sess_ver))
return;
_HA_ATOMIC_INC(&fe->fe_counters.cum_sess_ver[http_ver - 1]);
_HA_ATOMIC_INC(&fe->fe_counters.shared->tg[tgid - 1]->cum_sess_ver[http_ver - 1]);
if (l && l->counters)
_HA_ATOMIC_INC(&l->counters->cum_sess_ver[http_ver - 1]);
_HA_ATOMIC_INC(&l->counters->shared->tg[tgid - 1]->cum_sess_ver[http_ver - 1]);
}
/* increase the number of cumulated streams on the designated backend */
static inline void proxy_inc_be_ctr(struct proxy *be)
{
_HA_ATOMIC_INC(&be->be_counters.cum_sess);
_HA_ATOMIC_INC(&be->be_counters.shared->tg[tgid - 1]->cum_sess);
update_freq_ctr(&be->be_counters.shared->tg[tgid - 1]->sess_per_sec, 1);
HA_ATOMIC_UPDATE_MAX(&be->be_counters.sps_max,
update_freq_ctr(&be->be_counters.sess_per_sec, 1));
update_freq_ctr(&be->be_counters._sess_per_sec, 1));
}
/* increase the number of cumulated requests on the designated frontend.
@ -180,14 +187,15 @@ static inline void proxy_inc_be_ctr(struct proxy *be)
static inline void proxy_inc_fe_req_ctr(struct listener *l, struct proxy *fe,
unsigned int http_ver)
{
if (http_ver >= sizeof(fe->fe_counters.p.http.cum_req) / sizeof(*fe->fe_counters.p.http.cum_req))
if (http_ver >= sizeof(fe->fe_counters.shared->tg[tgid - 1]->p.http.cum_req) / sizeof(*fe->fe_counters.shared->tg[tgid - 1]->p.http.cum_req))
return;
_HA_ATOMIC_INC(&fe->fe_counters.p.http.cum_req[http_ver]);
_HA_ATOMIC_INC(&fe->fe_counters.shared->tg[tgid - 1]->p.http.cum_req[http_ver]);
if (l && l->counters)
_HA_ATOMIC_INC(&l->counters->p.http.cum_req[http_ver]);
_HA_ATOMIC_INC(&l->counters->shared->tg[tgid - 1]->p.http.cum_req[http_ver]);
update_freq_ctr(&fe->fe_counters.shared->tg[tgid - 1]->req_per_sec, 1);
HA_ATOMIC_UPDATE_MAX(&fe->fe_counters.p.http.rps_max,
update_freq_ctr(&fe->fe_counters.req_per_sec, 1));
update_freq_ctr(&fe->fe_counters.p.http._req_per_sec, 1));
}
/* Returns non-zero if the proxy is configured to retry a request if we got that status, 0 otherwise */

View File

@ -8,6 +8,9 @@
size_t qcs_http_rcv_buf(struct qcs *qcs, struct buffer *buf, size_t count,
char *fin);
int qcs_http_handle_standalone_fin(struct qcs *qcs);
size_t qcs_http_snd_buf(struct qcs *qcs, struct buffer *buf, size_t count,
char *fin);

View File

@ -107,11 +107,11 @@ struct quic_cc_path {
/* Congestion window. */
uint64_t cwnd;
/* The current maximum congestion window value reached. */
uint64_t mcwnd;
/* The maximum congestion window value which can be reached. */
uint64_t max_cwnd;
/* Minimum congestion window. */
uint64_t min_cwnd;
uint64_t cwnd_last_max;
/* Max limit on congestion window size. */
uint64_t limit_max;
/* Min limit on congestion window size. */
uint64_t limit_min;
/* Prepared data to be sent (in bytes). */
uint64_t prep_in_flight;
/* Outstanding data (in bytes). */
@ -123,7 +123,7 @@ struct quic_cc_path {
uint32_t recovery_start_ts;
};
/* pacing can be optionnaly activated on top of the algorithm */
/* pacing can be optionally activated on top of the algorithm */
#define QUIC_CC_ALGO_FL_OPT_PACING 0x01
struct quic_cc_algo {

View File

@ -29,9 +29,11 @@
#include <haproxy/api.h>
#include <haproxy/buf.h>
#include <haproxy/chunk.h>
#include <haproxy/proto_quic.h>
#include <haproxy/quic_cc-t.h>
#include <haproxy/quic_conn-t.h>
#include <haproxy/quic_loss.h>
#include <haproxy/thread.h>
void quic_cc_init(struct quic_cc *cc, struct quic_cc_algo *algo, struct quic_conn *qc);
void quic_cc_event(struct quic_cc *cc, struct quic_cc_event *ev);
@ -91,9 +93,10 @@ static inline void quic_cc_path_init(struct quic_cc_path *path, int ipv4, unsign
*(size_t *)&path->mtu = max_dgram_sz;
path->initial_wnd = QUIC_MIN(10 * max_dgram_sz, QUIC_MAX(max_dgram_sz << 1, 14720U));
path->cwnd = path->initial_wnd;
path->mcwnd = path->cwnd;
path->max_cwnd = max_cwnd;
path->min_cwnd = max_dgram_sz << 1;
cshared_add(&quic_mem_diff, path->cwnd);
path->cwnd_last_max = path->cwnd;
path->limit_max = max_cwnd;
path->limit_min = max_dgram_sz << 1;
path->prep_in_flight = 0;
path->in_flight = 0;
path->ifae_pkts = 0;
@ -115,7 +118,9 @@ static inline size_t quic_cc_path_prep_data(struct quic_cc_path *path)
return path->cwnd - path->prep_in_flight;
}
int quic_cwnd_may_increase(const struct quic_cc_path *path);
void quic_cc_path_reset(struct quic_cc_path *path);
void quic_cc_path_set(struct quic_cc_path *path, uint64_t val);
void quic_cc_path_inc(struct quic_cc_path *path, uint64_t val);
#endif /* USE_QUIC */
#endif /* _PROTO_QUIC_CC_H */

View File

@ -28,22 +28,22 @@
#include <sys/socket.h>
#include <haproxy/cbuf-t.h>
#include <haproxy/list.h>
#include <haproxy/show_flags-t.h>
#include <import/ebtree-t.h>
#include <haproxy/api-t.h>
#include <haproxy/buf-t.h>
#include <haproxy/listener-t.h>
#include <haproxy/openssl-compat.h>
#include <haproxy/mux_quic-t.h>
#include <haproxy/quic_cid-t.h>
#include <haproxy/quic_cc-t.h>
#include <haproxy/quic_loss-t.h>
#include <haproxy/quic_frame-t.h>
#include <haproxy/quic_openssl_compat-t.h>
#include <haproxy/quic_stats-t.h>
#include <haproxy/quic_tls-t.h>
#include <haproxy/quic_tp-t.h>
#include <haproxy/task.h>
#include <import/ebtree-t.h>
#include <haproxy/show_flags-t.h>
#include <haproxy/ssl_sock-t.h>
#include <haproxy/task-t.h>
typedef unsigned long long ull;
@ -100,6 +100,9 @@ typedef unsigned long long ull;
/* Default congestion window size. 480 kB, equivalent to the legacy value which was 30*bufsize */
#define QUIC_DFLT_MAX_WINDOW_SIZE 491520
/* Default ratio applied for max-stream-data-bidi-remote derived from max-data */
#define QUIC_DFLT_FRONT_STREAM_DATA_RATIO 90
/*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
@ -225,6 +228,9 @@ struct quic_version {
extern const struct quic_version quic_versions[];
extern const size_t quic_versions_nb;
extern const struct quic_version *preferred_version;
extern const struct quic_version *quic_version_draft_29;
extern const struct quic_version *quic_version_1;
extern const struct quic_version *quic_version_2;
/* unused: 0x01 */
/* Flag the packet number space as requiring an ACK frame to be sent. */
@ -276,6 +282,10 @@ struct quic_conn_cntrs {
long long streams_blocked_uni; /* total number of times STREAMS_BLOCKED_UNI frame was received */
};
struct connection;
struct qcc;
struct qcc_app_ops;
#define QUIC_CONN_COMMON \
struct { \
/* Connection owned socket FD. */ \
@ -298,6 +308,7 @@ struct quic_conn_cntrs {
/* Number of received bytes. */ \
uint64_t rx; \
} bytes; \
size_t max_udp_payload; \
/* First DCID used by client on its Initial packet. */ \
struct quic_cid odcid; \
/* DCID of our endpoint - not updated when a new DCID is used */ \
@ -308,7 +319,7 @@ struct quic_conn_cntrs {
* with a connection \
*/ \
struct eb_root *cids; \
struct listener *li; /* only valid for frontend connections */ \
enum obj_type *target; \
/* Idle timer task */ \
struct task *idle_timer_task; \
unsigned int idle_expire; \
@ -331,7 +342,10 @@ struct quic_conn {
int tps_tls_ext;
int state;
enum qc_mux_state mux_state; /* status of the connection/mux layer */
#ifdef USE_QUIC_OPENSSL_COMPAT
#ifdef HAVE_OPENSSL_QUIC
uint32_t prot_level;
#endif
#if defined(USE_QUIC_OPENSSL_COMPAT) || defined(HAVE_OPENSSL_QUIC)
unsigned char enc_params[QUIC_TP_MAX_ENCLEN]; /* encoded QUIC transport parameters */
size_t enc_params_len;
#endif
@ -380,10 +394,10 @@ struct quic_conn {
/* RX buffer */
struct buffer buf;
struct list pkt_list;
struct {
/* Number of open or closed streams */
uint64_t nb_streams;
} strms[QCS_MAX_TYPES];
/* first unhandled streams ID, set by MUX after release */
uint64_t stream_max_uni;
uint64_t stream_max_bidi;
} rx;
struct {
struct quic_tls_kp prv_rx;
@ -446,6 +460,7 @@ struct quic_conn_closed {
#define QUIC_FL_CONN_HPKTNS_DCD (1U << 16) /* Handshake packet number space discarded */
#define QUIC_FL_CONN_PEER_VALIDATED_ADDR (1U << 17) /* Peer address is considered as validated for this connection. */
#define QUIC_FL_CONN_NO_TOKEN_RCVD (1U << 18) /* Client dit not send any token */
#define QUIC_FL_CONN_SCID_RECEIVED (1U << 19) /* (client only: first Initial received. */
/* gap here */
#define QUIC_FL_CONN_TO_KILL (1U << 24) /* Unusable connection, to be killed */
#define QUIC_FL_CONN_TX_TP_RECEIVED (1U << 25) /* Peer transport parameters have been received (used for the transmitting part) */

View File

@ -69,7 +69,8 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
struct quic_connection_id *conn_id,
struct sockaddr_storage *local_addr,
struct sockaddr_storage *peer_addr,
int server, int token, void *owner);
int token, void *owner,
struct connection *conn);
int quic_build_post_handshake_frames(struct quic_conn *qc);
const struct quic_version *qc_supported_version(uint32_t version);
int quic_peer_validated_addr(struct quic_conn *qc);
@ -127,7 +128,11 @@ static inline void quic_conn_mv_cids_to_cc_conn(struct quic_conn_closed *cc_conn
}
/* Allocate the underlying required memory for <ncbuf> non-contiguous buffer */
/* Allocate the underlying required memory for <ncbuf> non-contiguous buffer.
* Does nothing if buffer is already allocated.
*
* Returns the buffer instance or NULL on allocation failure.
*/
static inline struct ncbuf *quic_get_ncbuf(struct ncbuf *ncbuf)
{
struct buffer buf = BUF_NULL;
@ -135,8 +140,8 @@ static inline struct ncbuf *quic_get_ncbuf(struct ncbuf *ncbuf)
if (!ncb_is_null(ncbuf))
return ncbuf;
b_alloc(&buf, DB_MUX_RX);
BUG_ON(b_is_null(&buf));
if (!b_alloc(&buf, DB_MUX_RX))
return NULL;
*ncbuf = ncb_make(buf.area, buf.size, 0);
ncb_init(ncbuf, 0);
@ -159,6 +164,22 @@ static inline void quic_free_ncbuf(struct ncbuf *ncbuf)
*ncbuf = NCBUF_NULL;
}
/* Return the address of the QUIC counters attached to the proxy of
* the owner of the connection whose object type address is <o> for
* listener and servers, or NULL for others object type.
*/
static inline void *qc_counters(enum obj_type *o, const struct stats_module *m)
{
struct proxy *p;
struct listener *l = objt_listener(o);
struct server *s = objt_server(o);
p = l ? l->bind_conf->frontend :
s ? s->proxy : NULL;
return p ? EXTRA_COUNTERS_GET(p->extra_counters_fe, m) : NULL;
}
void chunk_frm_appendf(struct buffer *buf, const struct quic_frame *frm);
void quic_set_connection_close(struct quic_conn *qc, const struct quic_err err);
void quic_set_tls_alert(struct quic_conn *qc, int alert);
@ -170,7 +191,7 @@ int qc_notify_send(struct quic_conn *qc);
void qc_check_close_on_released_mux(struct quic_conn *qc);
void quic_conn_release(struct quic_conn *qc);
int quic_conn_release(struct quic_conn *qc);
void qc_kill_conn(struct quic_conn *qc);

View File

@ -5,7 +5,7 @@
#include <haproxy/quic_cc-t.h>
struct quic_pacer {
const struct quic_cc *cc; /* Congestion controler algo used for this connection */
const struct quic_cc *cc; /* Congestion controller algo used for this connection */
ullong cur; /* Nanosecond timestamp of the last credit reloading */
uint credit; /* Number of packets which can be emitted in a single burst */

Some files were not shown because too many files have changed in this diff Show More