Compare commits

...

66 commits

Author SHA1 Message Date
Egor Shestakov
f5f9c008b1 CLEANUP: initcall: adjust comments to INITCALL{0,1} macros
Some checks are pending
Contrib / build (push) Waiting to run
alpine/musl / gcc (push) Waiting to run
VTest / Generate Build Matrix (push) Waiting to run
VTest / (push) Blocked by required conditions
Windows / Windows, gcc, all features (push) Waiting to run
It is likely that copy-pasted text was not initially adjusted in the
comments, that is misleading regarding number of arguments.

No backport needed.
2026-02-11 09:10:56 +01:00
William Lallemand
ea92b0ef01 BUG/MINOR: ssl: SSL_CERT_DIR environment variable doesn't affect haproxy
Some checks are pending
Contrib / build (push) Waiting to run
alpine/musl / gcc (push) Waiting to run
VTest / Generate Build Matrix (push) Waiting to run
VTest / (push) Blocked by required conditions
Windows / Windows, gcc, all features (push) Waiting to run
The documentation of @system-ca specifies that one can overwrite the
value provided by the SSL Library using SSL_CERT_DIR.

However it seems like X509_get_default_cert_dir() is not affected by
this environment variable, and X509_get_default_cert_dir_env() need to
be used in order to get the variable name, and get the value manually.

This could be backported in every stable branches. Note that older
branches don't have the memprintf in ssl_sock.c.
2026-02-10 21:34:45 +01:00
William Lallemand
2ac0d12790 MINOR: startup: Add the SSL lib verify directory in haproxy -vv
SSL libraries built manually might lack the right
X509_get_default_cert_dir() value.

The common way to fix the problem is to build openssl with
./configure --openssldir=/etc/ssl/

In order to verify this setting, output it with haproxy -vv.
2026-02-10 21:06:38 +01:00
Willy Tarreau
c724693b95 MINOR: activity: allow to switch per-task lock/memory profiling at runtime
Some checks are pending
Contrib / build (push) Waiting to run
alpine/musl / gcc (push) Waiting to run
VTest / Generate Build Matrix (push) Waiting to run
VTest / (push) Blocked by required conditions
Windows / Windows, gcc, all features (push) Waiting to run
Given that we already have "set profiling task", it's easy to permit to
enable/disable the lock and/or memory profiling at run time. However, the
change will only be applied next time the task profiling will be switched
from off/auto to on.

The patch is very minor and is best viewed with git show -b because it
indents a whole block that moves in a "if" clause.

This can be backported to 3.3 along with the two previous patches.
2026-02-10 17:53:01 +01:00
Willy Tarreau
e2631ee5f7 MEDIUM: activity: apply and use new finegrained task profiling settings
In continuity of previous patch, this one makes use of the new profiling
flags. For this, based on the global "profiling" setting, when switching
profiling on, we set or clear two flags on the thread context,
TH_FL_TASK_PROFILING_L and TH_FL_TASK_PROFILING_M to indicate whether
lock profiling and/or malloc profiling are desired when profiling is
enabled. These flags are checked along with TH_FL_TASK_PROFILING to
decide when to collect time around a lock or a malloc. And by default
we're back to the behavior of 3.2 in that neither lock nor malloc times
are collected anymore.

This is sufficient to see the CPU usage spent in the VDSO to significantly
drop from 22% to 2.2% on a highly loaded system.

This should be backported to 3.3 along with the previous patch.
2026-02-10 17:52:59 +01:00
Willy Tarreau
a7b2353cb3 MINOR: activity: support setting/clearing lock/memory watching for task profiling
Damien Claisse reported in issue #3257 a performance regression between
3.2 and 3.3 when task profiling is enabled, more precisely in relation
with the following patches were merged:

  98cc815e3e ("MINOR: activity: collect time spent with a lock held for each task")
  503084643f ("MINOR: activity: collect time spent waiting on a lock for each task")
  9d8c2a888b ("MINOR: activity: collect CPU time spent on memory allocations for each task")

The issue mostly comes from the first patches. What happens is that the
local time is taken when entering and leaving each lock, which costs a
lot on a contended system. The problem here is the lack of finegrained
settings for lock and malloc profiling.

This patch introduces a better approach. The task profiler goes back to
its default behavior in on/auto modes, but the configuration now accepts
new extra options "lock", "no-lock", "memory", "no-memory" to precisely
indicate other timers to watch for each task when profiling turns on.

This is achieved by setting two new flags HA_PROF_TASKS_LOCK and
HA_PROF_TASKS_MEM in the global "profiling" variable.

This patch only parses the new values and assigns them to the global
variable from the config file for now. The doc was updated.
2026-02-10 17:47:02 +01:00
Willy Tarreau
3b45beb465 CLEANUP: haproxy: fix bad line wrapping in run_poll_loop()
Commit 3674afe8a0 ("BUG/MEDIUM: threads: Atomically set TH_FL_SLEEPING
and clr FL_NOTIFIED") accidentally left a strange-looking line wrapping
making one think of an editing mistake, let's fix it and keep it on a
single line given that even indented wrapping is almost as large.

This can be backported with the fix above till 2.8 to keep the patch
context consistent between versions.
2026-02-10 14:11:42 +01:00
Willy Tarreau
64c5d45a26 BUG/MEDIUM: lb-chash: always properly initialize lb_nodes with dynamic servers
Some checks are pending
Contrib / build (push) Waiting to run
alpine/musl / gcc (push) Waiting to run
VTest / Generate Build Matrix (push) Waiting to run
VTest / (push) Blocked by required conditions
Windows / Windows, gcc, all features (push) Waiting to run
An issue was introduced in 3.0 with commit faa8c3e024 ("MEDIUM: lb-chash:
Deterministic node hashes based on server address"): the new server_key
field and lb_nodes entries initialization were not updated for servers
added at run time with "add server": server_key remains zero and the key
used in lb_node remains the one depending only on the server's ID.

This will cause trouble when adding new servers with consistent hashing,
because the hash-key will be ignored until the server's weight changes
and the key difference is detected, leading to its recalculation.

This is essentially caused by the poorly placed lb_nodes initialization
that is specific to lb-chash and had to be replicated in the code dealing
with server addition.

This commit solves the problem by adding a new ->server_init() function
in the lbprm proxy struct, that is called by the server addition code.
This also allows to abandon the complex check for LB algos that was
placed there for that purpose. For now only lb-chash provides such a
function, and calls it as well during initial setup. This way newly
added servers always use the correct key now.

While it should also theoretically have had an impact on servers added
with the "random" algorithm, it's unlikely that the difference between
proper server keys and those based on their ID could have had any visible
effect.

This patch should be backported as far as 3.0. The backport may be eased
by a preliminary backport of previous commit "CLEANUP: lb-chash: free
lb_nodes from chash's deinit(), not global", though this is not strictly
necessary if context is manually adjusted.
2026-02-10 07:22:54 +01:00
Willy Tarreau
62239539bf CLEANUP: lb-chash: free lb_nodes from chash's deinit(), not global
There's an ambuity on the ownership of lb_nodes in chash, it's allocated
by chash but freed by the server code in srv_free_params() from srv_drop()
upon deinit. Let's move this free() call to a chash-specific function
which will own the responsibility for doing this instead. Note that
the .server_deinit() callback is properly called both on proxy being
taken down and on server deletion.
2026-02-10 07:20:50 +01:00
Amaury Denoyelle
91a5b67b25 BUG/MINOR: proxy: fix default ALPN bind settings
Some checks failed
Contrib / build (push) Has been cancelled
alpine/musl / gcc (push) Has been cancelled
VTest / Generate Build Matrix (push) Has been cancelled
Windows / Windows, gcc, all features (push) Has been cancelled
VTest / (push) Has been cancelled
For "add backend" implementation, postparsing code in
check_config_validity() from cfgparse.c has been extracted in a new
dedicated function named proxy_finalize() into proxy.c.

This has caused unexpected compilation issue as in the latter file
TLSEXT_TYPE_application_layer_protocol_negotiation macro may be
undefined, in particular when building without QUIC support. Thus, code
related to default ALPN on binds is discarded after the preprocessing
stage.

Fix this by including openssl-compat header file into proxy source file.
This should be sufficient to ensure SSL related defines are properly
included.

This should fix recent issues on SSL regtests.

No need to backport.
2026-02-09 13:52:25 +01:00
Willy Tarreau
ecffaa6d5a MINOR: net_helper: extend the ip.fp output with an option presence mask
Emeric suggested that it's sometimes convenient to instantly know if a
client has advertised support for window scaling or timestamps for
example. While the info is present in the TCP options output, it's hard
to extract since it respects the options order.

So here we're extending the 56-bit fingerprint with 8 extra bits that
indicate the presence of options 2..8, and any option above 9 for the
last bit. In practice this is sufficient since higher options are not
commonly used. Also TCP option 5 is normally not sent on the SYN (SACK,
only SACK_perm is sent), and echo options 6 & 7 are no longer used
(replaced with timestamps). These fields might be repurposed in the
future if some more meaningful options are to be mapped (e.g. MPTCP,
TFO cookie, auth).
2026-02-09 09:18:04 +01:00
Amaury Denoyelle
a1db464c3e BUG/MINOR: proxy: fix null dereference in "add backend" handler
When a backend is created at runtime, the new proxy instance is inserted
at the end of proxies_list. This operation is buggy if this list is
empty : the code causes a null dereference which will lead to a crash.
This causes the following compilation error :

  CC      src/proxy.o
src/proxy.c: In function 'cli_parse_add_backend':
src/proxy.c:4933:36: warning: null pointer dereference [-Wnull-dereference]
 4933 |                 proxies_list->next = px;
      |                 ~~~~~~~~~~~~~~~~~~~^~~~

This patch fixes this issue. Note that in reality it cannot occur at
this moment as proxies_list cannot be empty (haproxy requires at least
one frontend to start, and the list also always contains internal
proxies).

No need to backport.
2026-02-06 21:35:12 +01:00
Amaury Denoyelle
5dff6e439d BUG/MINOR: proxy: fix clang build error on "add backend" handler
This patch fixes the following compilation error :
  src/proxy.c:4954:12: error: format string is not a string literal
        (potentially insecure) [-Werror,-Wformat-security]
   4954 |         ha_notice(msg);
        |                   ^~~

No need to backport.
2026-02-06 21:17:18 +01:00
Amaury Denoyelle
d7cdd2c7f4 REGTESTS: add dynamic backend creation test
Add a new regtests to validate backend creation at runtime. A server is
then added and requests made to validate the newly created instance
before (with force-be-switch) and after publishing.
2026-02-06 17:28:27 +01:00
Amaury Denoyelle
5753c14e84 MINOR: proxy: assign dynamic proxy ID
Implement proxy ID generation for dynamic backends. This is performed
through the already function existing proxy_get_next_id().

As an optimization, lookup will performed starting from a global
variable <dynpx_next_id>. It is initialized to the greatest ID assigned
after parsing, and updated each time a backend instance is created. When
backend deletion will be implemented, it could be lowered to the newly
available slot.
2026-02-06 17:28:27 +01:00
Amaury Denoyelle
3115eb82a6 MEDIUM: proxy: implement dynamic backend creation
Implement the required operations for "add backend" handler. This
requires a new proxy allocation, settings copy from the specified
default instance and proxy config finalization. All handlers registered
via REGISTER_POST_PROXY_CHECK() are also called on the newly created
instance.

If no error were encountered, the newly created proxy is finally
attached in the proxies list.
2026-02-06 17:28:27 +01:00
Amaury Denoyelle
07195a1af4 MINOR: proxy: check default proxy compatibility on "add backend"
This commits completes "add backend" handler with some checks performed
on the specified default proxy instance. These are additional checks
outside of the already existing inheritance rules, specific to dynamic
backends.

For now, a default proxy is considered not compatible if it is not in
mode TCP/HTTP. Also, a default proxy is rejected if it references HTTP
errors. This limitation may be lifted in the future, when HTTP errors
are partiallay reworked.
2026-02-06 17:28:26 +01:00
Amaury Denoyelle
a603811aac MINOR: proxy: parse guid on dynamic backend creation
Defines an extra optional GUID argument for "add backend" command. This
can be useful as it is not possible to define it via a default proxy
instance.
2026-02-06 17:28:04 +01:00
Amaury Denoyelle
e152913327 MINOR: proxy: parse mode on dynamic backend creation
Add an optional "mode" argument to "add backend" CLI command. This
argument allows to specify if the backend is in TCP or HTTP mode.

By default, it is mandatory, unless the inherited default proxy already
explicitely specifies the mode. To differentiate if TCP mode is implicit
or explicit, a new proxy flag PR_FL_DEF_EXPLICIT_MODE is defined. It is
set for every defaults instances which explicitely defined their mode.
2026-02-06 17:27:50 +01:00
Amaury Denoyelle
7ac5088c50 MINOR: proxy: define "add backend" handler
Define a basic CLI handler for "add backend".

For now, this handler only performs a parsing of the name argument and
return an error if a duplicate already exists. It runs under thread
isolation, to guarantee thread safety during the proxy creation.

This feature is considered in development. CLI command requires to set
experimental-mode.
2026-02-06 17:26:55 +01:00
Amaury Denoyelle
817003aa31 MINOR: backend: add function to check support for dynamic servers
Move backend compatibility checks performed during 'add server' in a
dedicated function be_supports_dynamic_srv(). This should simplify
addition of future restriction.

This function will be reused when implementing backend creation at
runtime.
2026-02-06 14:35:19 +01:00
Amaury Denoyelle
dc6cf224dd MINOR: proxy: refactor mode parsing
Define a new utility function str_to_proxy_mode() which is able to
convert a string into the corresponding proxy mode if possible. This new
function is used for the parsing of "mode" configuration proxy keyword.

This patch will be reused for dynamic backend implementation, in order
to parse a similar "mode" argument via a CLI handler.
2026-02-06 14:35:18 +01:00
Amaury Denoyelle
87ea407cce MINOR: proxy: refactor proxy inheritance of a defaults section
If a proxy is referencing a defaults instance, some checks must be
performed to ensure that inheritance will be compatible. Refcount of the
defaults instance may also be incremented if some settings cannot be
copied. This operation is performed when parsing a new proxy of defaults
section which references a defaults, either implicitely or explicitely.

This patch extracts this code into a dedicated function named
proxy_ref_defaults(). This in turn may call defaults_px_ref()
(previously called proxy_ref_defaults()) to increment its refcount.

The objective of this patch is to be able to reuse defaults inheritance
validation for dynamic backends created at runtime, outside of the
parsing code.
2026-02-06 14:35:18 +01:00
Amaury Denoyelle
a8bc83bea5 MINOR: cfgparse: move proxy post-init in a dedicated function
A lot of proxies initialization code is delayed on post-parsing stage,
as it depends on the configuration fully parsed. This is performed via a
loop on proxies_list.

Extract this code in a dedicated function proxy_finalize(). This patch
will be useful for dynamic backends creation.

Note that for the moment the code has been extracted as-is. With each
new features, some init code was added there. This has become a giant
loop with no real ordering. A future patch may provide some cleanup in
order to reorganize this.
2026-02-06 14:35:18 +01:00
Amaury Denoyelle
2c8ad11b73 MINOR: cfgparse: validate defaults proxies separately
Default proxies validation occurs during post-parsing. The objective is
to report any tcp/http-rules which could not behave as expected.

Previously, this was performed while looping over standard proxies list,
when such proxy is referencing a default instance. This was enough as
only named referenced proxies were kept after parsing. However, this is
not the case anymore in the context of dynamic backends creation at
runtime.

As such, this patch now performs validation on every named defaults
outside of the standard proxies list loop. This should not cause any
behavior difference, as defaults are validated without using the proxy
which relies on it.

Along with this change, PR_FL_READY proxy flag is now removed. Its usage
was only really needed for defaults, to avoid validating a same instance
multiple times. With the validation of defaults in their own loop, it is
now redundant.
2026-02-06 14:35:18 +01:00
Egor Shestakov
2a07dc9c24 BUG/MINOR: startup: handle a possible strdup() failure
Fix unhandled strdup() failure when initializing global.log_tag.

Bug was introduced with the fix UAF for global progname pointer from
351ae5dbe. So it must be backported as far as 3.1.
2026-02-06 10:50:31 +01:00
Egor Shestakov
9dd7cf769e BUG/MINOR: startup: fix allocation error message of progname string
Initially when init_early was introduced the progname string was a local
used for temporary storage of log_tag. Now it's global and detached from
log_tag enough. Thus, in the past we could inform that log_tag
allocation has been failed but not now.

Must be backported since the progname string became global, that is
v3.1-dev9-96-g49772c55e
2026-02-06 10:50:31 +01:00
Olivier Houchard
bf7a2808fc BUG/MEDIUM: threads: Differ checking the max threads per group number
Differ checking the max threads per group number until we're done
parsing the configuration file, as it may be set after a "thread-group-
directive. Otherwise the default value of 64 will be used, even if there
is a max-threads-per-group directive.

This should be backported to 3.3.
2026-02-06 03:01:50 +01:00
Olivier Houchard
9766211cf0 BUG/MINOR: threads: Initialize maxthrpertgroup earlier.
Give global.maxthrpertgroup its default value at global creation,
instead of later when we're trying to detect the thread count.
It is used when verifying the configuration file validity, and if it was
not set in the config file, in a few corner cases, the value of 0 would
be used, which would then reject perfectly fine configuration files.

This should be backported to 3.3.
2026-02-06 03:01:36 +01:00
William Lallemand
9e023ae930 DOC: internals: addd mworker V3 internals
Document the mworker V3 implementation introduced in HAProxy 3.1.
Explains the rationale behind moving configuration parsing out of the
master process to improve robustness.

Could be backported to 3.1.
2026-02-04 16:39:44 +01:00
Willy Tarreau
68e9fb73fd [RELEASE] Released version 3.4-dev4
Released version 3.4-dev4 with the following main changes :
    - BUG/MEDIUM: hlua: fix invalid lua_pcall() usage in hlua_traceback()
    - BUG/MINOR: hlua: consume error object if ignored after a failing lua_pcall()
    - BUG/MINOR: promex: Detach promex from the server on error dump its metrics dump
    - BUG/MEDIUM: mux-h1: Skip UNUSED htx block when formating the start line
    - BUG/MINOR: proto_tcp: Properly report support for HAVE_TCP_MD5SIG feature
    - BUG/MINOR: config: check capture pool creations for failures
    - BUG/MINOR: stick-tables: abort startup on stk_ctr pool creation failure
    - MEDIUM: pools: better check for size rounding overflow on registration
    - DOC: reg-tests: update VTest upstream link in the starting guide
    - BUG/MINOR: ssl: Properly manage alloc failures in SSL passphrase callback
    - BUG/MINOR: ssl: Encrypted keys could not be loaded when given alongside certificate
    - MINOR: ssl: display libssl errors on private key loading
    - BUG/MAJOR: applet: Don't call I/O handler if the applet was shut
    - MINOR: ssl: allow to disable certificate compression
    - BUG/MINOR: ssl: fix error message of tune.ssl.certificate-compression
    - DOC: config: mention some possible TLS versions restrictions for kTLS
    - OPTIM: server: move queueslength in server struct
    - OPTIM: proxy: separate queues fields from served
    - OPTIM: server: get rid of the last use of _ha_barrier_full()
    - DOC: config: mention that idle connection sharing is per thread-group
    - MEDIUM: h1: strictly verify quoting in chunk extensions
    - BUG/MINOR: config/ssl: fix spelling of "expose-experimental-directives"
    - BUG/MEDIUM: ssl: fix msg callbacks on QUIC connections
    - MEDIUM: ssl: remove connection from msg callback args
    - MEDIUM: ssl: porting to X509_STORE_get1_objects() for OpenSSL 4.0
    - REGTESTS: ssl: make reg-tests compatible with OpenSSL 4.0
    - DOC: internals: cleanup few typos in master-worker documentation
    - BUG/MEDIUM: applet: Fix test on shut flags for legacy applets
    - MINOR: quic: Fix build with USE_QUIC_OPENSSL_COMPAT
    - MEDIUM: tcpcheck: add post-80 option for mysql-check to support MySQL 8.x
    - BUG/MEDIUM: threads: Atomically set TH_FL_SLEEPING and clr FL_NOTIFIED
    - BUG/MINOR: cpu-topo: count cores not cpus to distinguish core types
    - DOC: config: mention the limitation on server id range for consistent hash
    - MEDIUM: backend: make "balance random" consider req rate when loads are equal
    - BUG/MINOR: config: Fix setting of alt_proto
2026-02-04 14:59:47 +01:00
Aperence
143f5a5c0d BUG/MINOR: config: Fix setting of alt_proto
This patch fixes the bug presented in issue #3254
(https://github.com/haproxy/haproxy/issues/3254), which
occured on FreeBSD when using a stream socket for in
nameserver section. This bug occured due to an incorrect
reset of the alt_proto for a stream socket when the default
socket is created as a datagram socket. This patch fixes
this bug by doing a late assignment to alt_proto when
a datagram socket is requested, leaving only the modification
of alt_proto done by mptcp. Additional documentation
for the use of alt_proto has also been added to
clarify the use of the alt_proto variable.
2026-02-04 14:54:20 +01:00
Willy Tarreau
b6bdb2553b MEDIUM: backend: make "balance random" consider req rate when loads are equal
As reported by Damien Claisse and Cdric Paillet, the "random" LB
algorithm can become particularly unfair with large numbers of servers
having few connections. It's indeed fairly common to see many servers
with zero connection in a thousand-server large farm, and in this case
the P2C algo consisting in checking the servers' loads doesn't help at
all and is basically similar to random(1). In this case, we only rely
on the distribution of server IDs in the random space to pick the best
server, but it's possible to observe huge discrepancies.

An attempt to model the problem clearly shows that with 1600 servers
with weight 10, for 1 million requests, the lowest loaded ones will
take 300 req while the most loaded ones will get 780, with most of
the values between 520 and 700.

In addition, only the first 28 lower bits of server IDs are used for
the key calculation, which means that node keys are more determinist.
Setting random keys in the lowest 28 bits only better packs values
with min around 530 and max around 710, with values mostly between
550 and 680.

This can only be compensated by increasing weights and draws without
being a perfect fix either. At 4 draws, the min is around 560 and the
max around 670, with most values bteween 590 and 650.

This patch takes another approach to this problem: when servers are on
tie regarding their loads, instead of arbitrarily taking the second one,
we now compare their current request rates, which is updated all the
time and smoothed over one second, and we pick the server with the
lowest request rate. Now with 2 draws, the curve is mostly flat, with
the min at 580 and the max at 628, and almost all values between 611
and 625. And 4 draws exclusively gives values from 614 to 624.

Other points will need to be addressed separately (bits of server ID,
maybe refine the hash algorithm), but these ones would affect how
caches are selected, and cannot be changed without an extra option.
For random however we can perform a change without impacting anyone.

This should be backported, probably only to 3.3 since it's where the
"random" algo became the default.
2026-02-04 14:54:16 +01:00
Willy Tarreau
3edf600859 DOC: config: mention the limitation on server id range for consistent hash
When using "hash-type consistent", we default to using the server's ID
as the insertion key. However, that key is scaled to avoid collisions
when inserting multiple slots for a server (16 per weight unit), and
that scaling loses the 4 topmost bits of the ID, so the only effective
range of IDs is 1..268435456, and anything above will provide the same
hashing keys again.

Let's mention this in the documentation, and also remind that it can
affect "balance random". This can be backported to all versions.
2026-02-04 14:54:16 +01:00
Willy Tarreau
cddeea58cd BUG/MINOR: cpu-topo: count cores not cpus to distinguish core types
The per-cpu capacity of a cluster was taken into account since 3.2 with
commit 6c88e27cf4 ("MEDIUM: cpu-topo: change "performance" to consider
per-core capacity").

In cpu_policy_performance() and cpu_policy_efficiency(), we're trying
to figure which cores have more capacity than others by comparing their
cluster's average capacity. However, contrary to what the comment says,
we're not averaging per core but per cpu, which makes a difference for
CPUs mixing SMT with non-SMT cores on the same SoC, such as intel's 14th
gen CPUs. Indeed, on a machine where cpufreq is not enabled, all CPUs
can be reported with a capacity of 1024, resulting in a big cluster of
16*1024, and 4 small clusters of 4*1024 each, giving an average of 1024
per CPU, making it impossible to distinguish one from the other. In this
situation, both "cpu-policy performance" and "cpu-policy efficiency"
enable all cores.

But this is wrong, what needs to be taken into account in the divide is
the number of *cores*, not *cpus*, that allows to distinguish big from
little clusters. This was not noticeable on the ARM machines the commit
above aimed at fixing because there, the number of CPUs equals the number
of cores. And on an x86 machine with cpu_freq enabled, the frequencies
continue to help spotting which ones are big/little.

By using nb_cores instead of nb_cpus in the comparison and in the avg_capa
compare function, it properly works again on x86 without affecting other
machines with 1 CPU per core.

This can be backported to 3.2.
2026-02-04 08:49:18 +01:00
Olivier Houchard
3674afe8a0 BUG/MEDIUM: threads: Atomically set TH_FL_SLEEPING and clr FL_NOTIFIED
When we're about to enter polling, atomically set TH_FL_SLEEPING and
remove TH_FL_NOTIFIED, instead of doing it in sequence. Otherwise,
another thread may sett that both the TH_FL_SLEEPING and the
TH_FL_NOTIFIED bits are set, and don't wake up the thread then it should
be doing that.
This prevents a bug where a thread is sleeping while it should be
handling a new connection, which can happen if there are very few
incoming connection. This is easy to reproduce when using only two
threads, and injecting with only one connection, the connection may then
never be handled.

This should be backported up to 2.8.
2026-02-04 07:13:06 +01:00
Hyeonggeun Oh
2527d9dcd1 MEDIUM: tcpcheck: add post-80 option for mysql-check to support MySQL 8.x
Some checks failed
Contrib / build (push) Has been cancelled
alpine/musl / gcc (push) Has been cancelled
VTest / Generate Build Matrix (push) Has been cancelled
Windows / Windows, gcc, all features (push) Has been cancelled
VTest / (push) Has been cancelled
This patch adds a new 'post-80' option that sets the
CLIENT_PLUGIN_AUTH (0x00080000) capability flag
and explicitly specifies mysql_native_password as
the authentication plugin in the handshake response.

This patch also addes documentation content for post-80 option
support in MySQL 8.x version. Which handles new default auth
plugin caching_sha2_password.

MySQL 8.0 changed the default authentication plugin from
mysql_native_password to caching_sha2_password.
The current mysql-check implementation only supports pre-41
and post-41 client auth protocols, which lack the CLIENT_PLUGIN_AUTH
capability flag. When HAProxy sends a post-41 authentication
packet to a MySQL 8.x server, the server responds with error 1251:
"Client does not support authentication protocol requested by server".

The new client capabilities for post-80 are:
- CLIENT_PROTOCOL_41 (0x00000200)
- CLIENT_SECURE_CONNECTION (0x00008000)
- CLIENT_PLUGIN_AUTH (0x00080000)

Usage example:
backend mysql_servers
	option mysql-check user haproxy post-80
	server db1 192.168.1.10:3306 check

The health check user must be created with mysql_native_password:
CREATE USER 'haproxy'@'%' IDENTIFIED WITH mysql_native_password BY '';

This addresses https://github.com/haproxy/haproxy/issues/2934.
2026-02-03 07:36:53 +01:00
Olivier Houchard
f26562bcb7 MINOR: quic: Fix build with USE_QUIC_OPENSSL_COMPAT
Commit fa094d0b61 changed the msg callback
args, but forgot to fix quic_tls_msg_callback() accordingly, so do that,
and remove the unused struct connection paramter.
2026-02-03 04:05:34 +01:00
Christopher Faulet
abc1947e19 BUG/MEDIUM: applet: Fix test on shut flags for legacy applets
Some checks failed
Contrib / build (push) Has been cancelled
alpine/musl / gcc (push) Has been cancelled
VTest / Generate Build Matrix (push) Has been cancelled
Windows / Windows, gcc, all features (push) Has been cancelled
VTest / (push) Has been cancelled
A regression was introduced in the commit 0ea601127 ("BUG/MAJOR: applet: Don't
call I/O handler if the applet was shut"). The test on shut flags for legacy
applets is inverted.

It should be harmeless on 3.4 and 3.3 because all applets were converted. But
this fix is mandatory for 3.2 and older.

The patch must be backported as far as 3.0 with the commit above.
2026-01-30 09:55:18 +01:00
Egor Shestakov
02e6375017 DOC: internals: cleanup few typos in master-worker documentation
Some checks are pending
Contrib / build (push) Waiting to run
alpine/musl / gcc (push) Waiting to run
VTest / Generate Build Matrix (push) Waiting to run
VTest / (push) Blocked by required conditions
Windows / Windows, gcc, all features (push) Waiting to run
s/mecanism/mechanism
s/got ride/got rid
s/traditionnal/traditional

One typo is confusion between master and worker that results to a
semantic mistake in the sentence:

"...the master will emit an "exit-on-failure" error and will kill every
workers with a SIGTERM and exits with the same error code than the
failed [-master-]{+worker+}..."

Should be backported as far as 3.1.
2026-01-29 18:44:40 +01:00
William Lallemand
da728aa0f6 REGTESTS: ssl: make reg-tests compatible with OpenSSL 4.0
OpenSSL 4.0 changed the way it stores objects in X509_STORE structures
and are not allowing anymore to iterate on objects in insertion order.

Meaning that the order of the object are not the same before and after
OpenSSL 4.0, and the reg-tests need to handle both cases.
2026-01-29 17:08:45 +01:00
William Lallemand
23e8ed6ea6 MEDIUM: ssl: porting to X509_STORE_get1_objects() for OpenSSL 4.0
OpenSSL 4.0 is deprecating X509_STORE_get0_objects().

Every occurence of X509_STORE_get0_objects() was first replaced by
X509_STORE_get1_objects().
This changes the ref count of the STACK_OF(X509_OBJECT) everywhere, and
need it to be sk_X509_OBJECT_pop_free(objs, X509_OBJECT_free) each time.

X509_STORE_get1_objects() is not available in AWS-LC, OpenSSL < 3.2,
LibreSSL and WolfSSL, so we need to still be compatible with get0.
To achieve this, 2 macros were added X509_STORE_getX_objects() and
sk_X509_OBJECT_popX_free(), these macros will use either the get0 or the
get1 macro depending on their availability. In the case of get0,
sk_X509_OBJECT_popX_free() will just do nothing instead of trying to
free.

Don't backport that unless really needed if we want to be compatible
with OpenSSL 4.0. It changes all the refcounts.
2026-01-29 17:08:41 +01:00
Amaury Denoyelle
fa094d0b61 MEDIUM: ssl: remove connection from msg callback args
Some checks failed
Contrib / build (push) Has been cancelled
alpine/musl / gcc (push) Has been cancelled
VTest / Generate Build Matrix (push) Has been cancelled
Windows / Windows, gcc, all features (push) Has been cancelled
VTest / (push) Has been cancelled
SSL msg callbacks are used for notification about sent/received SSL
messages. Such callbacks are registered via
ssl_sock_register_msg_callback().

Prior to this patch, connection was passed as first argument of these
callbacks. However, most of them do not use it. Worst, this may lead to
confusion as connection can be NULL in QUIC context.

This patch cleans this by removing connection argument. As an
alternative, connection can be retrieved in callbacks if needed using
ssl_sock_get_conn() but the code must be ready to deal with potential
NULL instances. As an example, heartbeat parsing callback has been
adjusted in this manner.
2026-01-29 11:14:09 +01:00
Amaury Denoyelle
869a997a68 BUG/MEDIUM: ssl: fix msg callbacks on QUIC connections
With QUIC backend implementation, SSL code has been adjusted in several
place when accessing connection instance. Indeed, with QUIC usage, SSL
context is tied up to quic_conn, and code may be executed prior/after
connection instantiation. For example, on frontend side, connection is
only created after QUIC handshake completion.

The following patch tried to fix unsafe accesses to connection. In
particular, msg callbacks are not called anymore if connection is NULL.

  fab7da0fd0
  BUG/MEDIUM: quic-be/ssl_sock: TLS callback called without connection

However, most msg callbacks do not need to use the connection instance.
The only occurence where it is accessed is for heartbeat message
parsing, which is the only case of crash solved. The above fix is too
restrictive as it completely prevents execution of these callbacks when
connection is unset. This breaks several features with QUIC, such as SSL
key logging or samples based on ClientHello capture.

The current patch reverts the above one. Thus, this restores invokation
of msg callbacks for QUIC during the whole low-level connection
lifetime. This requires a small adjustment in heartbeat parsing callback
to prevent access on a NULL connection.

The issue on ClientHello capture was mentionned in github issue #2495.

This must be backported up to 3.3.
2026-01-29 11:14:09 +01:00
Willy Tarreau
48d9c90ff2 BUG/MINOR: config/ssl: fix spelling of "expose-experimental-directives"
The help message for "ktls" mentions "expose-experimental-directive"
without the final 's', which is particularly annoying when copy-pasting
the directive from the error message directly into the config.

This should be backported to 3.3.
2026-01-29 11:07:55 +01:00
Willy Tarreau
35d63cc3c7 MEDIUM: h1: strictly verify quoting in chunk extensions
Some checks are pending
Contrib / build (push) Waiting to run
alpine/musl / gcc (push) Waiting to run
VTest / Generate Build Matrix (push) Waiting to run
VTest / (push) Blocked by required conditions
Windows / Windows, gcc, all features (push) Waiting to run
As reported by Ben Kallus in the following thread:

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

there exist some agents which mistakenly accept CRLF inside quoted
chunk extensions, making it possible to fool them by injecting one
extra chunk they won't see for example, or making them miss the end
of the body depending on how it's done. Haproxy, like most other
agents nowadays, doesn't care at all about chunk extensions and just
drops them, in agreement with the spec.

However, as discussed, since chunk extensions are basically never used
except for attacks, and that the cost of just matching quote pairs and
checking backslashed quotes is escape consistency remains relatively
low, it can make sense to add such a check to abort the message parsing
when this situation is encountered. Note that it has to be done at two
places, because there is a fast path and a slow path for chunk parsing.

Also note that it *will* cause transfers using improperly formatted chunk
extensions to fail, but since these are really not used, and that the
likelihood of them being used but improperly quoted certainly is much
lower than the risk of crossing a broken parser on the client's request
path or on the server's response path, we consider the risk as
acceptable. The test is not subject to the configurable parser exceptions
and it's very unlikely that it will ever be needed.

Since this is done in 3.4 which will be LTS, this patch will have to be
backported to 3.3 so that any unlikely trouble gets a chance to be
detected before users upgrade to 3.4.

Thanks to Ben for the discussion, and to Rajat Raghav for sparking it
in the first place even though the original report was mistaken.

Cc: Ben Kallus <benjamin.p.kallus.gr@dartmouth.edu>
Cc: Rajat Raghav <xclow3n@gmail.com>
Cc: Christopher Faulet <cfaulet@haproxy.com>
2026-01-28 18:54:23 +01:00
Willy Tarreau
bb36836d76 DOC: config: mention that idle connection sharing is per thread-group
There's already a tunable "tune.idle-pool.shared" allowing to enable or
disable idle connection sharing between threads. However the doc does not
mention that these connections are only shared between threads of the same
thread group, since 2.7 with commit 15c5500b6e ("MEDIUM: conn: make
conn_backend_get always scan the same group"). Let's clarify this and
also give a hint about "max-threads-per-group" which can be helpful for
machines with unified caches.
2026-01-28 17:18:50 +01:00
Willy Tarreau
a79a67b52f OPTIM: server: get rid of the last use of _ha_barrier_full()
The code in srv_add_to_idle_list() has its roots in 2.0 with commit
9ea5d361ae ("MEDIUM: servers: Reorganize the way idle connections are
cleaned."). At this era we didn't yet have the current set of atomic
load/store operations and we used to perform loads using volatile casts
after a barrier. It turns out that this function has kept this schema
over the years, resulting in a big mfence stalling all the pipeline
in the function:

       |     static __inline void
       |     __ha_barrier_full(void)
       |     {
       |     __asm __volatile("mfence" ::: "memory");
 27.08 |       mfence
       |     if ((volatile void *)srv->idle_node.node.leaf_p == NULL) {
  0.84 |       cmpq    $0x0,0x158(%r15)
  0.74 |       je      35f
       |     return 1;

Switching these for a pair of atomic loads got rid of this and brought
0.5 to 3% extra performance depending on the tests due to variations
elsewhere, but it has never been below 0.5%. Note that the second load
doesn't need to be atomic since it's protected by the lock, but it's
cleaner from an API and code review perspective. That's also why it's
relaxed.

This was the last user of _ha_barrier_full(), let's try not to
reintroduce it now!
2026-01-28 16:07:27 +00:00
Willy Tarreau
a9df6947b4 OPTIM: proxy: separate queues fields from served
There's still a lot of contention when accessing the backend's
totpend and queueslength for every request in may_dequeue_tasks(),
even when queues are not used. This only happens because it's stored
in the same cache line as >beconn which is being written by other
threads:

  0.01 |        call       sess_change_server
  0.02 |        mov        0x188(%r15),%esi     ## s->queueslength
       |      if (may_dequeue_tasks(srv, s->be))
  0.00 |        mov        0xa8(%r12),%rax
  0.00 |        mov        -0x50(%rbp),%r11d
  0.00 |        mov        -0x60(%rbp),%r10
  0.00 |        test       %esi,%esi
       |        jne        3349
  0.01 |        mov        0xa00(%rax),%ecx     ## p->queueslength
  8.26 |        test       %ecx,%ecx
  4.08 |        je         288d

This patch moves queueslength and totpend to their own cache line,
thus adding 64 bytes to the struct proxy, but gaining 3.6% of RPS
on a 64-core EPYC thanks to the elimination of this false sharing.

process_stream() goes down from 3.88% to 3.26% in perf top, with
the next top users being inc/dec (s->served) and be->beconn.
2026-01-28 16:07:27 +00:00
Willy Tarreau
3ca2a83fc0 OPTIM: server: move queueslength in server struct
This field is shared by all threads and must be in the shared area
instead, because where it's placed, it slows down access to other
fields of the struct by false sharing. Just moving this field gives
a steady 2% gain on the request rate (1.93 to 1.96 Mrps) on a 64-core
EPYC.
2026-01-28 16:07:27 +00:00
Willy Tarreau
cb3fd012cd DOC: config: mention some possible TLS versions restrictions for kTLS
Some checks are pending
Contrib / build (push) Waiting to run
alpine/musl / gcc (push) Waiting to run
VTest / Generate Build Matrix (push) Waiting to run
VTest / (push) Blocked by required conditions
Windows / Windows, gcc, all features (push) Waiting to run
It took me one hour of trial and fail to figure that kTLS and splicing
were not used only for reasons of TLS version, and that switching to
TLS v1.2 solved the issue. Thus, let's mention it in the doc so that
others find it more easily in the future.

This should be backported to 3.3.
2026-01-28 10:45:21 +01:00
William Lallemand
bbab0ac4d0 BUG/MINOR: ssl: fix error message of tune.ssl.certificate-compression
Some checks are pending
Contrib / build (push) Waiting to run
alpine/musl / gcc (push) Waiting to run
VTest / Generate Build Matrix (push) Waiting to run
VTest / (push) Blocked by required conditions
Windows / Windows, gcc, all features (push) Waiting to run
tune.ssl-certificate-compression expects 'auto' but not 'on'.

Could be backported if the previous patch is backported.
2026-01-27 16:25:11 +01:00
William Lallemand
6995fe60c3 MINOR: ssl: allow to disable certificate compression
This option allows to disable the certificate compression (RFC 8879)
using OpenSSL >= 3.2.0.

This feature is known to permit some denial of services by causing extra
memory allocations of approximately 22MiB and extra CPU work per
connection with OpenSSL versions affected by CVE-2025-66199.
( https://openssl-library.org/news/vulnerabilities/index.html#CVE-2025-66199 )

Setting this to "off" permits to mitigate the problem.

Must be backported to every stable branches.
2026-01-27 16:10:41 +01:00
Christopher Faulet
0ea601127e BUG/MAJOR: applet: Don't call I/O handler if the applet was shut
In 3.0, it was stated an applet could not be woken up after it was shutdown.
So the corresponding test in the applets I/O handler was removed. However,
it seems it may happen, especially when outgoing data are blocked on the
opposite side. But it is really unexpected because the "release" callback
function was already called and the appctx context was most probably
released.

Strangely, it was never detected by any applet till now. But the Prometheus
exporter was never updated and was still testing the shutdown. But when it
was refactored to use the new applet API in 3.3, the test was removed. And
this introduced a regression leading a crash because a server object could
be corrupted. Conditions to hit the bug are not really clear however.

So, now, to avoid any issue with all other applets, the test is performed in
task_process_applet(). The I/O handler is no longer called if the applet is
already shut.

The same is performed for applets still relying on the old API.

An amazing thanks to @idl0r for his invaluable help on this issue !

This patch should fix the issue #3244. It should first be backported to 3.3
and then slowly as far as 3.0.
2026-01-27 16:00:23 +01:00
William Lallemand
0ebef67132 MINOR: ssl: display libssl errors on private key loading
Some checks failed
Contrib / build (push) Has been cancelled
alpine/musl / gcc (push) Has been cancelled
VTest / Generate Build Matrix (push) Has been cancelled
Windows / Windows, gcc, all features (push) Has been cancelled
VTest / (push) Has been cancelled
Display a more precise error message from the libssl when a private key
can't be loaded correctly.
2026-01-26 14:19:19 +01:00
Remi Tricot-Le Breton
9b1faee4c9 BUG/MINOR: ssl: Encrypted keys could not be loaded when given alongside certificate
The SSL passphrase callback function was only called when loading
private keys from a dedicated file (separate from the corresponding
certificate) but not when both the certificate and the key were in the
same file.
We can now load them properly, regardless of how they are provided.
A flas had to be added in the 'passphrase_cb_data' structure because in
the 'ssl_sock_load_pem_into_ckch' function, when calling
'PEM_read_bio_PrivateKey' there might be no private key in the PEM file
which would mean that the callback never gets called (and cannot set the
'passphrase_idx' to -1).

This patch can be backported to 3.3.
2026-01-26 14:09:13 +01:00
Remi Tricot-Le Breton
d2ccc19fde BUG/MINOR: ssl: Properly manage alloc failures in SSL passphrase callback
Some error paths in 'ssl_sock_passwd_cb' (allocation failures) did not
set the 'passphrase_idx' to -1 which is the way for the caller to know
not to call the callback again so in some memory contention contexts we
could end up calling the callback 'infinitely' (or until memory is
finally available).

This patch must be backported to 3.3.
2026-01-26 14:08:50 +01:00
Egor Shestakov
f4cd1e74ba DOC: reg-tests: update VTest upstream link in the starting guide
The starting guide to regression testing had an outdated VTest link.

Could be backported as far as 2.6.
2026-01-26 13:56:13 +01:00
Willy Tarreau
1a3252e956 MEDIUM: pools: better check for size rounding overflow on registration
Certain object sizes cannot be controlled at declaration time because
the resulting object size may be slightly extended (tag, caller),
aligned and rounded up, or even doubled depending on pool settings
(e.g. if backup is used).

This patch addresses this by enlarging the type in the pool registration
to 64-bit so that no info is lost from the declaration, and extra checks
for overflows can be performed during registration after various rounding
steps. This allows to catch issues such as these ones and to report a
suitable error:

  global
      tune.http.logurilen 2147483647

  frontend
      capture request header name len 2147483647
      http-request capture src len 2147483647
      tcp-request content capture src len 2147483647
2026-01-26 11:54:14 +01:00
Willy Tarreau
e9e4821db5 BUG/MINOR: stick-tables: abort startup on stk_ctr pool creation failure
Since 3.3 with commit 945aa0ea82 ("MINOR: initcalls: Add a new initcall
stage, STG_INIT_2"), stkt_late_init() calls stkt_create_stk_ctr_pool()
but doesn't check its return value, so if the pool creation fails, the
process still starts, which is not correct. This patch adds a check for
the return value to make sure we fail to start in this case. This was
not an issue before 3.3 because the function was called as a post-check
handler which did check for errors in the returned values.
2026-01-26 11:45:49 +01:00
Willy Tarreau
4e7c07736a BUG/MINOR: config: check capture pool creations for failures
A few capture pools can fail in case of too large values for example.
These include the req_uri, capture, and caphdr pools, and may be triggered
with "tune.http.logurilen 2147483647" in the global section, or one of
these in a frontend:

  capture request header name len 2147483647
  http-request capture src len 2147483647
  tcp-request content capture src len 2147483647

These seem to be the only occurrences where create_pool()'s return value
is assigned without being checked, so let's add the proper check for
errors there. This can be backported as a hardening measure though the
risks and impacts are extremely low.
2026-01-26 11:45:49 +01:00
Christopher Faulet
c267d24f57 BUG/MINOR: proto_tcp: Properly report support for HAVE_TCP_MD5SIG feature
Some checks failed
Contrib / build (push) Has been cancelled
alpine/musl / gcc (push) Has been cancelled
VTest / Generate Build Matrix (push) Has been cancelled
Windows / Windows, gcc, all features (push) Has been cancelled
VTest / (push) Has been cancelled
Condition to report the support for HAVE_TCP_MD5SIG feature was inverted. It
is only an issue for the reg-test related to this feature.

This patch must be backported to 3.3.
2026-01-23 11:40:54 +01:00
Christopher Faulet
a3e9a04435 BUG/MEDIUM: mux-h1: Skip UNUSED htx block when formating the start line
UNUSED blocks were not properly handled when the H1 multiplexer was
formatting the start line of a request or a response. UNUSED was ignored but
not removed from HTX message. So the mux can loop infinitly on such block.

It could be seen a a major issue but in fact it happens only if a very
specific case on the reponse processing (at least I think so): the server
must send an interim message (a 100-continue for intance) with the final
response. HAProxy must receive both in same time and the final reponse must
be intercepted (via a http-response return action for instance), In that
case, the interim message is fowarded and the server final reponse is
removed and replaced by a proxy error message.

Now UNUSED htx blocks are properly skipped and removed.

This patch must be backported as far as 3.0.
2026-01-23 11:40:54 +01:00
Christopher Faulet
be68ecc37d BUG/MINOR: promex: Detach promex from the server on error dump its metrics dump
If an error occurres during the dump of a metric for a server, we must take
care to detach promex from the watcher list for this server. It must be
performed explicitly because on error, the applet state (st1) is changed, so
it is not possible to detach it during the applet release stage.

This patch must be backported with b4f64c0ab ("BUG/MEDIUM: promex: server
iteration may rely on stale server") as far as 3.0. On older versions, 2.8
and 2.6, the watcher_detach() line must be changed by "srv_drop(ctx->p[1])".
2026-01-23 11:40:54 +01:00
Aurelien DARRAGON
a66b4881d7 BUG/MINOR: hlua: consume error object if ignored after a failing lua_pcall()
We frequently use lua_pcall() to provide safe alternative functions
(encapsulated helpers) that prevent the process from crashing in case
of Lua error when Lua is executed from an unsafe environment.

However, some of those safe helpers don't handle errors properly. In case
of error, the Lua API will always put an error object on top of the stack
as stated in the documentation. This error object can be used to retrieve
more info about the error. But in some cases when we ignore it, we should
still consume it to prevent the stack from being altered with an extra
object when returning from the helper function.

It should be backported to all stable versions. If the patch doesn't apply
automatically, all that's needed is to check for lua_pcall() in hlua.c
and for other cases than 'LUA_OK', make sure that the error object is popped
from the stack before the function returns.
2026-01-23 11:23:37 +01:00
Aurelien DARRAGON
9e9083d0e2 BUG/MEDIUM: hlua: fix invalid lua_pcall() usage in hlua_traceback()
Since commit 365ee28 ("BUG/MINOR: hlua: prevent LJMP in hlua_traceback()")
we now use lua_pcall() to protect sensitive parts of hlua_traceback()
function, and this to prevent Lua from crashing the process in case of
unexpected Lua error.

This is still relevant, but an error was made, as lua_pcall() was given
the nresult argument '1' when _hlua_traceback() internal function
doesn't push any argument on the stack. Because of this, it seems Lua
API still tries to push garbage object on top of the stack before
returning. This may cause functions that leverage hlua_traceback() in
the middle of stack manipulation to end up having a corrupted stack when
continuing after the hlua_traceback().

There doesn't seem to be many places where this could be a problem, as
this was discovered using the reproducer documented in f535d3e
("BUG/MEDIUM: debug: only dump Lua state when panicking"). Indeed, when
hlua_traceback() was used from the signal handler while the thread was
previously executing Lua, when returning to Lua after the handler the
Lua stack would be corrupted.

To fix the issue, we emphasize on the fact that the _hlua_traceback()
function doesn't push anything on the stack, returns 0, thus lua_pcall()
is given 0 'nresult' argument to prevent anything from being pushed after
the execution, preserving the original stack state.

This should be backported to all stable versions (because 365ee28 was
backported there)
2026-01-23 11:23:31 +01:00
54 changed files with 2526 additions and 1644 deletions

View file

@ -1,6 +1,43 @@
ChangeLog :
===========
2026/02/04 : 3.4-dev4
- BUG/MEDIUM: hlua: fix invalid lua_pcall() usage in hlua_traceback()
- BUG/MINOR: hlua: consume error object if ignored after a failing lua_pcall()
- BUG/MINOR: promex: Detach promex from the server on error dump its metrics dump
- BUG/MEDIUM: mux-h1: Skip UNUSED htx block when formating the start line
- BUG/MINOR: proto_tcp: Properly report support for HAVE_TCP_MD5SIG feature
- BUG/MINOR: config: check capture pool creations for failures
- BUG/MINOR: stick-tables: abort startup on stk_ctr pool creation failure
- MEDIUM: pools: better check for size rounding overflow on registration
- DOC: reg-tests: update VTest upstream link in the starting guide
- BUG/MINOR: ssl: Properly manage alloc failures in SSL passphrase callback
- BUG/MINOR: ssl: Encrypted keys could not be loaded when given alongside certificate
- MINOR: ssl: display libssl errors on private key loading
- BUG/MAJOR: applet: Don't call I/O handler if the applet was shut
- MINOR: ssl: allow to disable certificate compression
- BUG/MINOR: ssl: fix error message of tune.ssl.certificate-compression
- DOC: config: mention some possible TLS versions restrictions for kTLS
- OPTIM: server: move queueslength in server struct
- OPTIM: proxy: separate queues fields from served
- OPTIM: server: get rid of the last use of _ha_barrier_full()
- DOC: config: mention that idle connection sharing is per thread-group
- MEDIUM: h1: strictly verify quoting in chunk extensions
- BUG/MINOR: config/ssl: fix spelling of "expose-experimental-directives"
- BUG/MEDIUM: ssl: fix msg callbacks on QUIC connections
- MEDIUM: ssl: remove connection from msg callback args
- MEDIUM: ssl: porting to X509_STORE_get1_objects() for OpenSSL 4.0
- REGTESTS: ssl: make reg-tests compatible with OpenSSL 4.0
- DOC: internals: cleanup few typos in master-worker documentation
- BUG/MEDIUM: applet: Fix test on shut flags for legacy applets
- MINOR: quic: Fix build with USE_QUIC_OPENSSL_COMPAT
- MEDIUM: tcpcheck: add post-80 option for mysql-check to support MySQL 8.x
- BUG/MEDIUM: threads: Atomically set TH_FL_SLEEPING and clr FL_NOTIFIED
- BUG/MINOR: cpu-topo: count cores not cpus to distinguish core types
- DOC: config: mention the limitation on server id range for consistent hash
- MEDIUM: backend: make "balance random" consider req rate when loads are equal
- BUG/MINOR: config: Fix setting of alt_proto
2026/01/22 : 3.4-dev3
- BUILD: ssl: strchr definition changed in C23
- BUILD: tools: memchr definition changed in C23

View file

@ -1,2 +1,2 @@
$Format:%ci$
2026/01/22
2026/02/04

View file

@ -1 +1 @@
3.4-dev3
3.4-dev4

View file

@ -1255,7 +1255,7 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
labels[lb_idx].value = ist2(sv->id, strlen(sv->id));
if (!stats_fill_sv_line(px, sv, 0, stats, ST_I_PX_MAX, &(ctx->field_num)))
return -1;
goto error;
if ((ctx->flags & PROMEX_FL_NO_MAINT_SRV) && (sv->cur_admin & SRV_ADMF_MAINT))
goto next_sv;
@ -1473,7 +1473,7 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
counters = EXTRA_COUNTERS_GET(sv->extra_counters, mod);
if (!mod->fill_stats(counters, stats + ctx->field_num, &ctx->mod_field_num))
return -1;
goto error;
val = stats[ctx->field_num + ctx->mod_field_num];
metric.type = ((val.type == FN_GAUGE) ? PROMEX_MT_GAUGE : PROMEX_MT_COUNTER);
@ -1515,6 +1515,10 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
full:
ret = 0;
goto end;
error:
watcher_detach(&ctx->srv_watch);
return -1;
}
/* Dump metrics of module <mod>. It returns 1 on success, 0 if <out> is full and

View file

@ -3,7 +3,7 @@
Configuration Manual
----------------------
version 3.4
2026/01/22
2026/02/04
This document covers the configuration language as implemented in the version
@ -1983,6 +1983,7 @@ The following keywords are supported in the "global" section :
- tune.ssl.cachesize
- tune.ssl.capture-buffer-size
- tune.ssl.capture-cipherlist-size (deprecated)
- tune.ssl.certificate-compression
- tune.ssl.default-dh-param
- tune.ssl.force-private-cache
- tune.ssl.hard-maxrecord
@ -4004,7 +4005,7 @@ profiling.memory { on | off }
use in production. The same may be achieved at run time on the CLI using the
"set profiling memory" command, please consult the management manual.
profiling.tasks { auto | on | off }
profiling.tasks { auto | on | off | lock | no-lock | memory | no-memory }*
Enables ('on') or disables ('off') per-task CPU profiling. When set to 'auto'
the profiling automatically turns on a thread when it starts to suffer from
an average latency of 1000 microseconds or higher as reported in the
@ -4015,6 +4016,18 @@ profiling.tasks { auto | on | off }
systems, containers, or virtual machines, or when the system swaps (which
must absolutely never happen on a load balancer).
When task profiling is enabled, HAProxy can also collect the time each task
spends with a lock held or waiting for a lock, as well as the time spent
waiting for a memory allocation to succeed in case of a pool cache miss. This
can sometimes help understand certain causes of latency. For this, the extra
keywords "lock" (to enable lock time collection), "no-lock" (to disable it),
"memory" (to enable memory allocation time collection) or "no-memory" (to
disable it) may additionally be passed. By default they are not enabled since
they can have a non-negligible CPU impact on highly loaded systems (3-10%).
Note that the overhead is only taken when profiling is effectively running,
so that when running in "auto" mode, it will only appear when HAProxy decides
to turn it on.
CPU profiling per task can be very convenient to report where the time is
spent and which requests have what effect on which other request. Enabling
it will typically affect the overall's performance by less than 1%, thus it
@ -4526,7 +4539,11 @@ tune.idle-pool.shared { on | off }
disabling this option without setting a conservative value on "pool-low-conn"
for all servers relying on connection reuse to achieve a high performance
level, otherwise connections might be closed very often as the thread count
increases.
increases. Note that in any case, connections are only shared between threads
of the same thread group. This means that systems with many NUMA nodes may
show slightly more persistent connections while machines with unified caches
and many CPU cores per node may experience higher CPU usage. In the latter
case, the "max-thread-per-group" tunable may be used to improve the behavior.
tune.idletimer <timeout>
Sets the duration after which HAProxy will consider that an empty buffer is
@ -5310,6 +5327,22 @@ tune.ssl.capture-cipherlist-size <number> (deprecated)
formats. If the value is 0 (default value) the capture is disabled,
otherwise a buffer is allocated for each SSL/TLS connection.
tune.ssl.certificate-compression { auto | off }
This setting allows to configure the certificate compression support which is
an extension (RFC 8879) to TLS 1.3.
When set to "auto" it uses the default value of the TLS library.
With "off" it tries to explicitely disable the support of the feature.
HAProxy won't try to send compressed certificates anymore nor accept
compressed certificates.
Configures both backend and frontend sides.
This keyword is supported by OpenSSL >= 3.2.0.
The default value is auto.
tune.ssl.default-dh-param <number>
Sets the maximum size of the Diffie-Hellman parameters used for generating
the ephemeral/temporary Diffie-Hellman key in case of DHE key exchange. The
@ -6262,8 +6295,16 @@ balance url_param <param> [check_post]
will take away N-1 of the highest loaded servers at the
expense of performance. With very high values, the algorithm
will converge towards the leastconn's result but much slower.
In addition, for large server farms with very low loads (or
perfect balance), comparing loads will often lead to a tie,
so in case of equal loads between all measured servers, their
request rate over the last second are compared, which allows
to better balance server usage over time in the same spirit
as roundrobin does, and smooth consistent hash unfairness.
The default value is 2, which generally shows very good
distribution and performance. This algorithm is also known as
distribution and performance. For large farms with low loads
(less than a few requests per second per server), it may help
to raise it to 3 or even 4. This algorithm is also known as
the Power of Two Random Choices and is described here :
http://www.eecs.harvard.edu/~michaelm/postscripts/handbook2001.pdf
@ -10800,7 +10841,7 @@ no option logasap
logging.
option mysql-check [ user <username> [ { post-41 | pre-41 } ] ]
option mysql-check [ user <username> [ { post-41 | pre-41 | post-80 } ] ]
Use MySQL health checks for server testing
May be used in the following contexts: tcp
@ -10813,6 +10854,12 @@ option mysql-check [ user <username> [ { post-41 | pre-41 } ] ]
server.
post-41 Send post v4.1 client compatible checks (the default)
pre-41 Send pre v4.1 client compatible checks
post-80 Send post v8.0 client compatible checks with CLIENT_PLUGIN_AUTH
capability set and mysql_native_password as the authentication
plugin. Use this option when connecting to MySQL 8.0+ servers
where the health check user is created with mysql_native_password
authentication. Example:
CREATE USER 'haproxy'@'%' IDENTIFIED WITH mysql_native_password BY '';
If you specify a username, the check consists of sending two MySQL packet,
one Client Authentication packet, and one QUIT packet, to correctly close
@ -17203,7 +17250,9 @@ interface <interface>
ktls <on|off> [ EXPERIMENTAL ]
Enables or disables ktls for those sockets. If enabled, kTLS will be used
if the kernel supports it and the cipher is compatible. This is only
available on Linux kernel 4.17 and above.
available on Linux kernel 4.17 and above. Please note that some network
drivers and/or TLS stacks might restrict kTLS usage to TLS v1.2 only. See
also "force-tlsv12".
label <label>
Sets an optional label for these sockets. It could be used group sockets by
@ -18306,7 +18355,10 @@ hash-key <key>
id The node keys will be derived from the server's numeric
identifier as set from "id" or which defaults to its position
in the server list.
in the server list. This is the default. Note that only the 28
lowest bits of the ID will be used (i.e. (id % 268435456)), so
better only use values comprised between 1 and this value to
avoid overlap.
addr The node keys will be derived from the server's address, when
available, or else fall back on "id".
@ -18318,7 +18370,9 @@ hash-key <key>
HAProxy processes are balancing traffic to the same set of servers. If the
server order of each process is different (because, for example, DNS records
were resolved in different orders) then this will allow each independent
HAProxy processes to agree on routing decisions.
HAProxy processes to agree on routing decisions. Note: "balance random" also
uses "hash-type consistent", and the quality of the distribution will depend
on the quality of the keys.
id <value>
May be used in the following contexts: tcp, http, log
@ -18463,9 +18517,10 @@ See also: "option tcp-check", "option httpchk"
ktls <on|off> [ EXPERIMENTAL ]
May be used in the following contexts: tcp, http, log, peers, ring
Enables or disables ktls for those sockets. If enabled, kTLS will be used
if the kernel supports it and the cipher is compatible.
This is only available on Linux.
Enables or disables ktls for those sockets. If enabled, kTLS will be used if
the kernel supports it and the cipher is compatible. This is only available
on Linux 4.17 and above. Please note that some network drivers and/or TLS
stacks might restrict kTLS usage to TLS v1.2 only. See also "force-tlsv12".
log-bufsize <bufsize>
May be used in the following contexts: log
@ -21196,9 +21251,9 @@ ip.fp([<mode>])
can be used to distinguish between multiple apparently identical hosts. The
real-world use case is to refine the identification of misbehaving hosts
between a shared IP address to avoid blocking legitimate users when only one
is misbehaving and needs to be blocked. The converter builds a 7-byte binary
block based on the input. The bytes of the fingerprint are arranged like
this:
is misbehaving and needs to be blocked. The converter builds a 8-byte minimum
binary block based on the input. The bytes of the fingerprint are arranged
like this:
- byte 0: IP TOS field (see ip.tos)
- byte 1:
- bit 7: IPv6 (1) / IPv4 (0)
@ -21213,10 +21268,13 @@ ip.fp([<mode>])
- bits 3..0: TCP window scaling + 1 (1..15) / 0 (no WS advertised)
- byte 3..4: tcp.win
- byte 5..6: tcp.options.mss, or zero if absent
- byte 7: 1 bit per present TCP option, with options 2 to 8 being mapped to
bits 0..6 respectively, and bit 7 indicating the presence of any
option from 9 to 255.
The <mode> argument permits to append more information to the fingerprint. By
default, when the <mode> argument is not set or is zero, the fingerprint is
solely made of the 7 bytes described above. If <mode> is specified as another
solely made of the 8 bytes described above. If <mode> is specified as another
value, it then corresponds to the sum of the following values, and the
respective components will be concatenated to the fingerprint, in the order
below:
@ -21226,7 +21284,7 @@ ip.fp([<mode>])
- 4: the source IP address is appended to the fingerprint, which adds
4 bytes for IPv4 and 16 for IPv6.
Example: make a 12..24 bytes fingerprint using the base FP, the TTL and the
Example: make a 13..25 bytes fingerprint using the base FP, the TTL and the
source address (1+4=5):
frontend test

View file

@ -11,7 +11,7 @@ default init, this was controversial but fedora and archlinux already uses it.
At this time HAProxy still had a multi-process model, and the way haproxy is
working was incompatible with the daemon mode.
Systemd is compatible with traditionnal forking services, but somehow HAProxy
Systemd is compatible with traditional forking services, but somehow HAProxy
is different. To work correctly, systemd needs a main PID, this is the PID of
the process that systemd will supervises.
@ -45,7 +45,7 @@ However the wrapper suffered from several problems:
### mworker V1
HAProxy 1.8 got ride of the wrapper which was replaced by the master worker
HAProxy 1.8 got rid of the wrapper which was replaced by the master worker
mode. This first version was basically a reintegration of the wrapper features
within HAProxy. HAProxy is launched with the -W flag, read the configuration and
then fork. In mworker mode, the master is usually launched as a root process,
@ -86,7 +86,7 @@ retrieved automatically.
The master is supervising the workers, when a current worker (not a previous one
from before the reload) is exiting without being asked for a reload, the master
will emit an "exit-on-failure" error and will kill every workers with a SIGTERM
and exits with the same error code than the failed master, this behavior can be
and exits with the same error code than the failed worker, this behavior can be
changed by using the "no exit-on-failure" option in the global section.
While the master is supervising the workers using the wait() function, the
@ -186,8 +186,8 @@ number that can be found in HAPROXY_PROCESSES. With this change the stats socket
in the configuration is less useful and everything can be done from the master
CLI.
With 2.7, the reload mecanism of the master CLI evolved, with previous versions,
this mecanism was asynchronous, so once the `reload` command was received, the
With 2.7, the reload mechanism of the master CLI evolved, with previous versions,
this mechanism was asynchronous, so once the `reload` command was received, the
master would reload, the active master CLI connection was closed, and there was
no way to return a status as a response to the `reload` command. To achieve a
synchronous reload, a dedicated sockpair is used, one side uses a master CLI
@ -208,3 +208,38 @@ starts with -st to achieve a hard stop on the previous worker.
Version 3.0 got rid of the libsystemd dependencies for sd_notify() after the
events of xz/openssh, the function is now implemented directly in haproxy in
src/systemd.c.
### mworker V3
This version was implemented with HAProxy 3.1, the goal was to stop parsing and
applying the configuration in the master process.
One of the caveats of the previous implementation was that the parser could take
a lot of time, and the master process would be stuck in the parser instead of
handling its polling loop, signals etc. Some parts of the configuration parsing
could also be less reliable with third-party code (EXTRA_OBJS), it could, for
example, allow opening FDs and not closing them before the reload which
would crash the master after a few reloads.
The startup of the master-worker was reorganized this way:
- the "discovery" mode, which is a lighter configuration parsing step, only
applies the configuration which need to be effective for the master process.
For example, "master-worker", "mworker-max-reloads" and less than 20 other
keywords that are identified by KWF_DISCOVERY in the code. It is really fast
as it don't need all the configuration to be applied in the master process.
- the master will then fork a worker, with a PROC_O_INIT flag. This worker has
a temporary sockpair connected to the master CLI. Once the worker is forked,
the master initializes its configuration and starts its polling loop.
- The newly forked worker will try to parse the configuration, which could
result in a failure (exit 1), or any bad error code. In case of success, the
worker will send a "READY" message to the master CLI then close this FD. At
this step everything was initialized and the worker can enter its polling
loop.
- The master then waits for the worker, it could:
* receive the READY message over the mCLI, resulting in a successful loading
of haproxy
* receive a SIGCHLD, meaning the worker exited and couldn't load

View file

@ -1725,6 +1725,30 @@ add acl [@<ver>] <acl> <pattern>
This command cannot be used if the reference <acl> is a name also used with
a map. In this case, the "add map" command must be used instead.
add backend <name> from <defproxy> [mode <mode>] [guid <guid>] [ EXPERIMENTAL ]
Instantiate a new backend proxy with the name <name>.
Only TCP or HTTP proxies can be created. All of the settings are inherited
from <defproxy> default proxy instance. By default, it is mandatory to
specify the backend mode via the argument of the same name, unless <defproxy>
already defines it explicitely. It is also possible to use an optional GUID
argument if wanted.
Servers can be added via the command "add server". The backend is initialized
in the unpublished state. Once considered ready for traffic, use "publish
backend" to expose the newly created instance.
All named default proxies can be used, given that they validate the same
inheritance rules applied during configuration parsing. There is some
exceptions though, for example when the mode is neither TCP nor HTTP. Another
exception is that it is not yet possible to use a default proxies which
reference custom HTTP errors, for example via the errorfiles or http-rules
keywords.
This command is restricted and can only be issued on sockets configured for
level "admin". Moreover, this feature is still considered in development so it
also requires experimental mode (see "experimental-mode on").
add map [@<ver>] <map> <key> <value>
add map [@<ver>] <map> <payload>
Add an entry into the map <map> to associate the value <value> to the key
@ -2534,7 +2558,8 @@ set maxconn global <maxconn>
delayed until the threshold is reached. A value of zero restores the initial
setting.
set profiling { tasks | memory } { auto | on | off }
set profiling memory { on | off }
set profiling tasks { auto | on | off | lock | no-lock | memory | no-memory }
Enables or disables CPU or memory profiling for the indicated subsystem. This
is equivalent to setting or clearing the "profiling" settings in the "global"
section of the configuration file. Please also see "show profiling". Note
@ -2544,6 +2569,13 @@ set profiling { tasks | memory } { auto | on | off }
on the linux-glibc target), and requires USE_MEMORY_PROFILING to be set at
compile time.
. For tasks profiling, it is possible to enable or disable the collection of
per-task lock and memory timings at runtime, but the change is only taken
into account next time the profiler switches from off/auto to on (either
automatically or manually). Thus when using "no-lock" to disable per-task
lock profiling and save CPU cycles, it is recommended to flip the task
profiling off then on to commit the change.
set rate-limit connections global <value>
Change the process-wide connection rate limit, which is set by the global
'maxconnrate' setting. A value of zero disables the limitation. This limit

View file

@ -24,7 +24,7 @@ vtest installation
------------------------
To use vtest you will have to download and compile the recent vtest
sources found at https://github.com/vtest/VTest.
sources found at https://github.com/vtest/VTest2.
To compile vtest:

View file

@ -33,6 +33,8 @@
#define HA_PROF_TASKS_MASK 0x00000003 /* per-task CPU profiling mask */
#define HA_PROF_MEMORY 0x00000004 /* memory profiling */
#define HA_PROF_TASKS_MEM 0x00000008 /* per-task CPU profiling with memory */
#define HA_PROF_TASKS_LOCK 0x00000010 /* per-task CPU profiling with locks */
#ifdef USE_MEMORY_PROFILING

View file

@ -192,6 +192,7 @@ struct lbprm {
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 */
int (*server_init)(struct server *); /* initialize a freshly added server (runtime); <0=fail. */
};
#endif /* _HAPROXY_BACKEND_T_H */

View file

@ -69,6 +69,7 @@ int backend_parse_balance(const char **args, char **err, struct proxy *curproxy)
int tcp_persist_rdp_cookie(struct stream *s, struct channel *req, int an_bit);
int be_downtime(struct proxy *px);
int be_supports_dynamic_srv(struct proxy *px, char **msg);
void recount_servers(struct proxy *px);
void update_backend_weight(struct proxy *px);

View file

@ -263,6 +263,8 @@ static inline int h1_parse_chunk_size(const struct buffer *buf, int start, int s
const char *ptr_old = ptr;
const char *end = b_wrap(buf);
uint64_t chunk = 0;
int backslash = 0;
int quote = 0;
stop -= start; // bytes left
start = stop; // bytes to transfer
@ -327,13 +329,37 @@ static inline int h1_parse_chunk_size(const struct buffer *buf, int start, int s
if (--stop == 0)
return 0;
while (!HTTP_IS_CRLF(*ptr)) {
/* The loop seeks the first CRLF or non-tab CTL char
* and stops there. If a backslash/quote is active,
* it's an error. If none, we assume it's the CRLF
* and go back to the top of the loop checking for
* CR then LF. This way CTLs, lone LF etc are handled
* in the fallback path. This allows to protect
* remotes against their own possibly non-compliant
* chunk-ext parser which could mistakenly skip a
* quoted CRLF. Chunk-ext are not used anyway, except
* by attacks.
*/
while (!HTTP_IS_CTL(*ptr) || HTTP_IS_SPHT(*ptr)) {
if (backslash)
backslash = 0; // escaped char
else if (*ptr == '\\' && quote)
backslash = 1;
else if (*ptr == '\\') // backslash not permitted outside quotes
goto error;
else if (*ptr == '"') // begin/end of quoted-pair
quote = !quote;
if (++ptr >= end)
ptr = b_orig(buf);
if (--stop == 0)
return 0;
}
/* we have a CRLF now, loop above */
/* mismatched quotes / backslashes end here */
if (quote || backslash)
goto error;
/* CTLs (CRLF) fall to the common check */
continue;
}
else

View file

@ -147,14 +147,14 @@ __attribute__((constructor)) static void __initcb_##linenum() \
#define _DECLARE_INITCALL(...) \
__DECLARE_INITCALL(__VA_ARGS__)
/* This requires that function <function> is called with pointer argument
* <argument> during init stage <stage> which must be one of init_stage.
/* This requires that function <function> is called without arguments
* during init stage <stage> which must be one of init_stage.
*/
#define INITCALL0(stage, function) \
_DECLARE_INITCALL(stage, __LINE__, function, 0, 0, 0)
/* This requires that function <function> is called with pointer argument
* <argument> during init stage <stage> which must be one of init_stage.
* <arg1> during init stage <stage> which must be one of init_stage.
*/
#define INITCALL1(stage, function, arg1) \
_DECLARE_INITCALL(stage, __LINE__, function, arg1, 0, 0)

View file

@ -380,6 +380,14 @@ static inline unsigned long ERR_peek_error_func(const char **func)
#endif
#if (HA_OPENSSL_VERSION_NUMBER >= 0x40000000L) && !defined(OPENSSL_IS_AWSLC) && !defined(LIBRESSL_VERSION_NUMBER) && !defined(USE_OPENSSL_WOLFSSL)
# define X509_STORE_getX_objects(x) X509_STORE_get1_objects(x)
# define sk_X509_OBJECT_popX_free(x, y) sk_X509_OBJECT_pop_free(x,y)
#else
# define X509_STORE_getX_objects(x) X509_STORE_get0_objects(x)
# define sk_X509_OBJECT_popX_free(x, y) ({})
#endif
#if (HA_OPENSSL_VERSION_NUMBER >= 0x1010000fL) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x2070200fL)
#define __OPENSSL_110_CONST__ const
#else

View file

@ -72,8 +72,8 @@ struct pool_registration {
struct list list; /* link element */
const char *name; /* name of the pool */
const char *file; /* where the pool is declared */
ullong size; /* expected object size */
unsigned int line; /* line in the file where the pool is declared, 0 if none */
unsigned int size; /* expected object size */
unsigned int flags; /* MEM_F_* */
unsigned int type_align; /* type-imposed alignment; 0=unspecified */
unsigned int align; /* expected alignment; 0=unspecified */

View file

@ -183,7 +183,7 @@ unsigned long long pool_total_allocated(void);
unsigned long long pool_total_used(void);
void pool_flush(struct pool_head *pool);
void pool_gc(struct pool_head *pool_ctx);
struct pool_head *create_pool_with_loc(const char *name, unsigned int size, unsigned int align,
struct pool_head *create_pool_with_loc(const char *name, ullong size, unsigned int align,
unsigned int flags, const char *file, unsigned int line);
struct pool_head *create_pool_from_reg(const char *name, struct pool_registration *reg);
void create_pool_callback(struct pool_head **ptr, char *name, struct pool_registration *reg);

View file

@ -242,7 +242,7 @@ enum PR_SRV_STATE_FILE {
/* Proxy flags */
#define PR_FL_DISABLED 0x01 /* The proxy was disabled in the configuration (not at runtime) */
#define PR_FL_STOPPED 0x02 /* The proxy was stopped */
#define PR_FL_READY 0x04 /* The proxy is ready to be used (initialized and configured) */
#define PR_FL_DEF_EXPLICIT_MODE 0x04 /* Proxy mode is explicitely defined - only used for defaults instance */
#define PR_FL_EXPLICIT_REF 0x08 /* The default proxy is explicitly referenced by another proxy */
#define PR_FL_IMPLICIT_REF 0x10 /* The default proxy is implicitly referenced by another proxy */
#define PR_FL_PAUSED 0x20 /* The proxy was paused at run time (reversible) */
@ -508,10 +508,16 @@ struct proxy {
EXTRA_COUNTERS(extra_counters_be);
THREAD_ALIGN();
unsigned int queueslength; /* Sum of the length of each queue */
/* these ones change all the time */
int served; /* # of active sessions currently being served */
int totpend; /* total number of pending connections on this instance (for stats) */
unsigned int feconn, beconn; /* # of active frontend and backends streams */
THREAD_ALIGN();
/* these ones are only changed when queues are involved, but checked
* all the time.
*/
unsigned int queueslength; /* Sum of the length of each queue */
int totpend; /* total number of pending connections on this instance (for stats) */
};
struct switching_rule {

View file

@ -41,6 +41,8 @@ extern unsigned int error_snapshot_id; /* global ID assigned to each error then
extern struct ceb_root *proxy_by_name; /* tree of proxies sorted by name */
extern struct list defaults_list; /* all defaults proxies list */
extern unsigned int dynpx_next_id;
extern const struct cfg_opt cfg_opts[];
extern const struct cfg_opt cfg_opts2[];
extern const struct cfg_opt cfg_opts3[];
@ -59,6 +61,7 @@ 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);
enum pr_mode str_to_proxy_mode(const char *mode);
const char *proxy_find_best_option(const char *word, const char **extra);
uint proxy_get_next_id(uint from);
void proxy_store_name(struct proxy *px);
@ -74,8 +77,7 @@ void defaults_px_destroy_all_unref(void);
void defaults_px_detach(struct proxy *px);
void defaults_px_ref_all(void);
void defaults_px_unref_all(void);
void proxy_ref_defaults(struct proxy *px, struct proxy *defpx);
int proxy_ref_defaults(struct proxy *px, struct proxy *defpx, char **errmsg);
void proxy_unref_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,
@ -97,6 +99,7 @@ int resolve_stick_rule(struct proxy *curproxy, struct sticking_rule *mrule);
void free_stick_rules(struct list *rules);
void free_server_rules(struct list *srules);
int proxy_init_per_thr(struct proxy *px);
int proxy_finalize(struct proxy *px, int *err_code);
/*
* This function returns a string containing the type of the proxy in a format

View file

@ -20,8 +20,7 @@
#define QUIC_OPENSSL_COMPAT_CLIENT_APPLICATION "CLIENT_TRAFFIC_SECRET_0"
#define QUIC_OPENSSL_COMPAT_SERVER_APPLICATION "SERVER_TRAFFIC_SECRET_0"
void quic_tls_compat_msg_callback(struct connection *conn,
int write_p, int version, int content_type,
void quic_tls_compat_msg_callback(int write_p, int version, int content_type,
const void *buf, size_t len, SSL *ssl);
int quic_tls_compat_init(struct bind_conf *bind_conf, SSL_CTX *ctx);
void quic_tls_compat_keylog_callback(const SSL *ssl, const char *line);

View file

@ -383,7 +383,6 @@ struct server {
unsigned next_eweight; /* next pending eweight to commit */
unsigned cumulative_weight; /* weight of servers prior to this one in the same group, for chash balancing */
int maxqueue; /* maximum number of pending connections allowed */
unsigned int queueslength; /* Sum of the length of each queue */
int shard; /* shard (in peers protocol context only) */
int log_bufsize; /* implicit ring bufsize (for log server only - in log backend) */
@ -406,6 +405,7 @@ struct server {
unsigned int max_used_conns; /* Max number of used connections (the counter is reset at each connection purges */
unsigned int est_need_conns; /* Estimate on the number of needed connections (max of curr and previous max_used) */
unsigned int curr_sess_idle_conns; /* Current number of idle connections attached to a session instead of idle/safe trees. */
unsigned int queueslength; /* Sum of the length of each queue */
/* elements only used during boot, do not perturb and plug the hole */
struct guid_node guid; /* GUID global tree node */

View file

@ -80,6 +80,7 @@ void ssl_store_delete_cafile_entry(struct cafile_entry *ca_e);
int ssl_store_load_ca_from_buf(struct cafile_entry *ca_e, char *cert_buf, int append);
int ssl_store_load_locations_file(char *path, int create_if_none, enum cafile_type type);
int __ssl_store_load_locations_file(char *path, int create_if_none, enum cafile_type type, int shuterror);
const char *ha_default_cert_dir();
extern struct cert_exts cert_exts[];
extern int (*ssl_commit_crlfile_cb)(const char *path, X509_STORE *ctx, char **err);

View file

@ -194,7 +194,7 @@ struct issuer_chain {
struct connection;
typedef void (*ssl_sock_msg_callback_func)(struct connection *conn,
typedef void (*ssl_sock_msg_callback_func)(
int write_p, int version, int content_type,
const void *buf, size_t len, SSL *ssl);
@ -338,6 +338,8 @@ struct global_ssl {
int renegotiate; /* Renegotiate mode (SSL_RENEGOTIATE_ flag) */
char **passphrase_cmd;
int passphrase_cmd_args_cnt;
unsigned int certificate_compression:1; /* allow to explicitely disable certificate compression */
};
/* The order here matters for picking a default context,
@ -361,6 +363,7 @@ struct passphrase_cb_data {
const char *path;
struct ckch_data *ckch_data;
int passphrase_idx;
int callback_called;
};
#endif /* USE_OPENSSL */

View file

@ -362,15 +362,19 @@ static inline unsigned long thread_isolated()
extern uint64_t now_mono_time(void); \
if (_LK_ != _LK_UN) { \
th_ctx->lock_level += bal; \
if (unlikely(th_ctx->flags & TH_FL_TASK_PROFILING)) \
if (unlikely((th_ctx->flags & (TH_FL_TASK_PROFILING|TH_FL_TASK_PROFILING_L)) == \
(TH_FL_TASK_PROFILING|TH_FL_TASK_PROFILING_L))) \
lock_start = now_mono_time(); \
} \
(void)(expr); \
if (_LK_ == _LK_UN) { \
th_ctx->lock_level += bal; \
if (th_ctx->lock_level == 0 && unlikely(th_ctx->flags & TH_FL_TASK_PROFILING)) \
if (th_ctx->lock_level == 0 &&\
unlikely((th_ctx->flags & (TH_FL_TASK_PROFILING|TH_FL_TASK_PROFILING_L)) == \
(TH_FL_TASK_PROFILING|TH_FL_TASK_PROFILING_L))) \
th_ctx->locked_total += now_mono_time() - th_ctx->lock_start_date; \
} else if (unlikely(th_ctx->flags & TH_FL_TASK_PROFILING)) { \
} else if (unlikely((th_ctx->flags & (TH_FL_TASK_PROFILING|TH_FL_TASK_PROFILING_L)) == \
(TH_FL_TASK_PROFILING|TH_FL_TASK_PROFILING_L))) { \
uint64_t now = now_mono_time(); \
if (lock_start) \
th_ctx->lock_wait_total += now - lock_start; \
@ -384,7 +388,8 @@ static inline unsigned long thread_isolated()
typeof(expr) _expr = (expr); \
if (_expr == 0) { \
th_ctx->lock_level += bal; \
if (unlikely(th_ctx->flags & TH_FL_TASK_PROFILING)) { \
if (unlikely((th_ctx->flags & (TH_FL_TASK_PROFILING|TH_FL_TASK_PROFILING_L)) == \
(TH_FL_TASK_PROFILING|TH_FL_TASK_PROFILING_L))) { \
if (_LK_ == _LK_UN && th_ctx->lock_level == 0) \
th_ctx->locked_total += now_mono_time() - th_ctx->lock_start_date; \
else if (_LK_ != _LK_UN && th_ctx->lock_level == 1) \

View file

@ -69,6 +69,8 @@ enum {
#define TH_FL_IN_DBG_HANDLER 0x00000100 /* thread currently in the debug signal handler */
#define TH_FL_IN_WDT_HANDLER 0x00000200 /* thread currently in the wdt signal handler */
#define TH_FL_IN_ANY_HANDLER 0x00000380 /* mask to test if the thread is in any signal handler */
#define TH_FL_TASK_PROFILING_L 0x00000400 /* task profiling in locks (also requires TASK_PROFILING) */
#define TH_FL_TASK_PROFILING_M 0x00000800 /* task profiling in mem alloc (also requires TASK_PROFILING) */
/* we have 4 buffer-wait queues, in highest to lowest emergency order */
#define DYNBUF_NBQ 4

View file

@ -0,0 +1,84 @@
varnishtest "Add backend via cli"
feature ignore_unknown_macro
haproxy hsrv -conf {
global
.if feature(THREAD)
thread-groups 1
.endif
defaults
mode http
timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
frontend fe
bind "fd@${fe}"
http-request return status 200
} -start
haproxy h1 -conf {
global
.if feature(THREAD)
thread-groups 1
.endif
defaults
mode http
timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
frontend fe
bind "fd@${feS}"
force-be-switch if { req.hdr("x-admin") "1" }
use_backend %[req.hdr(x-be)]
defaults def
defaults def_http
mode http
} -start
client c1 -connect ${h1_feS_sock} {
txreq -hdr "x-be: be"
rxresp
expect resp.status == 503
} -run
haproxy h1 -cli {
# non existent backend
send "experimental-mode on; add backend be from def"
expect ~ "Mode is required"
send "experimental-mode on; add backend be from def_http"
expect ~ "New backend registered."
send "add server be/srv ${hsrv_fe_addr}:${hsrv_fe_port}"
expect ~ "New server registered."
send "enable server be/srv"
expect ~ ".*"
}
client c1 -connect ${h1_feS_sock} {
txreq -hdr "x-be: be"
rxresp
expect resp.status == 503
txreq -hdr "x-be: be" -hdr "x-admin: 1"
rxresp
expect resp.status == 200
} -run
haproxy h1 -cli {
send "publish backend be"
expect ~ "Backend published."
}
client c1 -connect ${h1_feS_sock} {
txreq -hdr "x-be: be"
rxresp
expect resp.status == 200
} -run

View file

@ -51,7 +51,7 @@ haproxy h1 -cli {
# invalid load-balancing algo
send "add server other/s1 ${s1_addr}:${s1_port}"
expect ~ "Backend must use a dynamic load balancing to support dynamic servers."
expect ~ "backend 'other' uses a non dynamic load balancing method"
# invalid mux proto
send "add server other2/s1 ${s1_addr}:${s1_port} proto h2"

View file

@ -145,7 +145,7 @@ haproxy h1 -cli {
send "show ssl ca-file ${testdir}/certs/set_cafile_interCA1.crt:2"
expect !~ ".*SHA1 FingerPrint: 4FFF535278883264693CEA72C4FAD13F995D0098"
send "show ssl ca-file ${testdir}/certs/set_cafile_interCA1.crt:2"
expect ~ ".*SHA1 FingerPrint: 3D3D1D10AD74A8135F05A818E10E5FA91433954D"
expect ~ ".*SHA1 FingerPrint: 3D3D1D10AD74A8135F05A818E10E5FA91433954D|5F8DAE4B2099A09F9BDDAFD7E9D900F0CE49977C"
}
client c1 -connect ${h1_clearverifiedlst_sock} {

View file

@ -86,9 +86,7 @@ haproxy h1 -cli {
expect ~ "\\*${testdir}/certs/interCA2_crl_empty.pem"
send "show ssl crl-file \\*${testdir}/certs/interCA2_crl_empty.pem"
expect ~ "Revoked Certificates:"
send "show ssl crl-file \\*${testdir}/certs/interCA2_crl_empty.pem:1"
expect ~ "Serial Number: 1008"
expect ~ "Revoked Certificates:\n.*Serial Number: 1008"
}
# This connection should still succeed since the transaction was not committed

View file

@ -659,8 +659,20 @@ void activity_count_runtime(uint32_t run_time)
if (!(_HA_ATOMIC_LOAD(&th_ctx->flags) & TH_FL_TASK_PROFILING)) {
if (unlikely((profiling & HA_PROF_TASKS_MASK) == HA_PROF_TASKS_ON ||
((profiling & HA_PROF_TASKS_MASK) == HA_PROF_TASKS_AON &&
swrate_avg(run_time, TIME_STATS_SAMPLES) >= up)))
swrate_avg(run_time, TIME_STATS_SAMPLES) >= up))) {
if (profiling & HA_PROF_TASKS_LOCK)
_HA_ATOMIC_OR(&th_ctx->flags, TH_FL_TASK_PROFILING_L);
else
_HA_ATOMIC_AND(&th_ctx->flags, ~TH_FL_TASK_PROFILING_L);
if (profiling & HA_PROF_TASKS_MEM)
_HA_ATOMIC_OR(&th_ctx->flags, TH_FL_TASK_PROFILING_M);
else
_HA_ATOMIC_AND(&th_ctx->flags, ~TH_FL_TASK_PROFILING_M);
_HA_ATOMIC_OR(&th_ctx->flags, TH_FL_TASK_PROFILING);
}
} else {
if (unlikely((profiling & HA_PROF_TASKS_MASK) == HA_PROF_TASKS_OFF ||
((profiling & HA_PROF_TASKS_MASK) == HA_PROF_TASKS_AOFF &&
@ -692,26 +704,41 @@ static int cfg_parse_prof_memory(char **args, int section_type, struct proxy *cu
}
#endif // USE_MEMORY_PROFILING
/* config parser for global "profiling.tasks", accepts "on" or "off" */
/* config parser for global "profiling.tasks", accepts "on", "off", 'auto",
* "lock", "no-lock", "memory", "no-memory".
*/
static int cfg_parse_prof_tasks(char **args, int section_type, struct proxy *curpx,
const struct proxy *defpx, const char *file, int line,
char **err)
{
if (too_many_args(1, args, err, NULL))
return -1;
int arg;
if (strcmp(args[1], "on") == 0) {
profiling = (profiling & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_ON;
HA_ATOMIC_STORE(&prof_task_start_ns, now_ns);
for (arg = 1; *args[arg]; arg++) {
if (strcmp(args[arg], "on") == 0) {
profiling = (profiling & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_ON;
HA_ATOMIC_STORE(&prof_task_start_ns, now_ns);
}
else if (strcmp(args[arg], "auto") == 0) {
profiling = (profiling & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_AOFF;
HA_ATOMIC_STORE(&prof_task_start_ns, now_ns);
}
else if (strcmp(args[arg], "off") == 0)
profiling = (profiling & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_OFF;
else if (strcmp(args[arg], "lock") == 0)
profiling |= HA_PROF_TASKS_LOCK;
else if (strcmp(args[arg], "no-lock") == 0)
profiling &= ~HA_PROF_TASKS_LOCK;
else if (strcmp(args[arg], "memory") == 0)
profiling |= HA_PROF_TASKS_MEM;
else if (strcmp(args[arg], "no-memory") == 0)
profiling &= ~HA_PROF_TASKS_MEM;
else
break;
}
else if (strcmp(args[1], "auto") == 0) {
profiling = (profiling & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_AOFF;
HA_ATOMIC_STORE(&prof_task_start_ns, now_ns);
}
else if (strcmp(args[1], "off") == 0)
profiling = (profiling & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_OFF;
else {
memprintf(err, "'%s' expects either 'on', 'auto', or 'off' but got '%s'.", args[0], args[1]);
/* either no arg or invalid arg */
if (arg == 1 || *args[arg]) {
memprintf(err, "'%s' expects a combination of either 'on', 'auto', 'off', 'lock', 'no-lock', 'memory', or 'no-memory', but got '%s'.", args[0], args[arg]);
return -1;
}
return 0;
@ -720,6 +747,8 @@ static int cfg_parse_prof_tasks(char **args, int section_type, struct proxy *cur
/* parse a "set profiling" command. It always returns 1. */
static int cli_parse_set_profiling(char **args, char *payload, struct appctx *appctx, void *private)
{
int arg;
if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
return 1;
@ -765,52 +794,66 @@ static int cli_parse_set_profiling(char **args, char *payload, struct appctx *ap
if (strcmp(args[2], "tasks") != 0)
return cli_err(appctx, "Expects either 'tasks' or 'memory'.\n");
if (strcmp(args[3], "on") == 0) {
unsigned int old = profiling;
int i;
for (arg = 3; *args[arg]; arg++) {
if (strcmp(args[arg], "on") == 0) {
unsigned int old = profiling;
int i;
while (!_HA_ATOMIC_CAS(&profiling, &old, (old & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_ON))
;
while (!_HA_ATOMIC_CAS(&profiling, &old, (old & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_ON))
;
HA_ATOMIC_STORE(&prof_task_start_ns, now_ns);
HA_ATOMIC_STORE(&prof_task_stop_ns, 0);
HA_ATOMIC_STORE(&prof_task_start_ns, now_ns);
HA_ATOMIC_STORE(&prof_task_stop_ns, 0);
/* also flush current profiling stats */
for (i = 0; i < SCHED_ACT_HASH_BUCKETS; i++) {
HA_ATOMIC_STORE(&sched_activity[i].calls, 0);
HA_ATOMIC_STORE(&sched_activity[i].cpu_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].lat_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].lkw_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].lkd_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].mem_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].func, NULL);
HA_ATOMIC_STORE(&sched_activity[i].caller, NULL);
/* also flush current profiling stats */
for (i = 0; i < SCHED_ACT_HASH_BUCKETS; i++) {
HA_ATOMIC_STORE(&sched_activity[i].calls, 0);
HA_ATOMIC_STORE(&sched_activity[i].cpu_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].lat_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].lkw_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].lkd_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].mem_time, 0);
HA_ATOMIC_STORE(&sched_activity[i].func, NULL);
HA_ATOMIC_STORE(&sched_activity[i].caller, NULL);
}
}
}
else if (strcmp(args[3], "auto") == 0) {
unsigned int old = profiling;
unsigned int new;
else if (strcmp(args[arg], "auto") == 0) {
unsigned int old = profiling;
unsigned int new;
do {
if ((old & HA_PROF_TASKS_MASK) >= HA_PROF_TASKS_AON)
new = (old & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_AON;
else
new = (old & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_AOFF;
} while (!_HA_ATOMIC_CAS(&profiling, &old, new));
do {
if ((old & HA_PROF_TASKS_MASK) >= HA_PROF_TASKS_AON)
new = (old & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_AON;
else
new = (old & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_AOFF;
} while (!_HA_ATOMIC_CAS(&profiling, &old, new));
HA_ATOMIC_STORE(&prof_task_start_ns, now_ns);
HA_ATOMIC_STORE(&prof_task_stop_ns, 0);
}
else if (strcmp(args[3], "off") == 0) {
unsigned int old = profiling;
while (!_HA_ATOMIC_CAS(&profiling, &old, (old & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_OFF))
;
HA_ATOMIC_STORE(&prof_task_start_ns, now_ns);
HA_ATOMIC_STORE(&prof_task_stop_ns, 0);
}
else if (strcmp(args[arg], "off") == 0) {
unsigned int old = profiling;
while (!_HA_ATOMIC_CAS(&profiling, &old, (old & ~HA_PROF_TASKS_MASK) | HA_PROF_TASKS_OFF))
;
if (HA_ATOMIC_LOAD(&prof_task_start_ns))
HA_ATOMIC_STORE(&prof_task_stop_ns, now_ns);
if (HA_ATOMIC_LOAD(&prof_task_start_ns))
HA_ATOMIC_STORE(&prof_task_stop_ns, now_ns);
}
else if (strcmp(args[arg], "lock") == 0)
HA_ATOMIC_OR(&profiling, HA_PROF_TASKS_LOCK);
else if (strcmp(args[arg], "no-lock") == 0)
HA_ATOMIC_AND(&profiling, ~HA_PROF_TASKS_LOCK);
else if (strcmp(args[arg], "memory") == 0)
HA_ATOMIC_OR(&profiling, HA_PROF_TASKS_MEM);
else if (strcmp(args[arg], "no-memory") == 0)
HA_ATOMIC_AND(&profiling, ~HA_PROF_TASKS_MEM);
else
break; // unknown arg
}
else
return cli_err(appctx, "Expects 'on', 'auto', or 'off'.\n");
/* either no arg or invalid one */
if (arg == 3 || *args[arg])
return cli_err(appctx, "Expects a combination of either 'on', 'auto', 'off', 'lock', 'no-lock', 'memory' or 'no-memory'.\n");
return 1;
}

View file

@ -848,7 +848,12 @@ struct task *task_run_applet(struct task *t, void *context, unsigned int state)
input = applet_output_data(app);
output = co_data(oc);
app->applet->fct(app);
/* Don't call I/O handler if the applet was shut (release callback was
* already called)
*/
if (!se_fl_test(app->sedesc, SE_FL_SHR | SE_FL_SHW))
app->applet->fct(app);
TRACE_POINT(APPLET_EV_PROCESS, app);
@ -945,7 +950,11 @@ struct task *task_process_applet(struct task *t, void *context, unsigned int sta
applet_need_more_data(app);
applet_have_no_more_data(app);
app->applet->fct(app);
/* Don't call I/O handler if the applet was shut (release callback was
* already called)
*/
if (!applet_fl_test(app, APPCTX_FL_SHUTDOWN))
app->applet->fct(app);
TRACE_POINT(APPLET_EV_PROCESS, app);

View file

@ -59,6 +59,7 @@
#include <haproxy/task.h>
#include <haproxy/ticks.h>
#include <haproxy/time.h>
#include <haproxy/tools.h>
#include <haproxy/trace.h>
#define TRACE_SOURCE &trace_strm
@ -576,9 +577,20 @@ struct server *get_server_rnd(struct stream *s, const struct server *avoid)
/* compare the new server to the previous best choice and pick
* the one with the least currently served requests.
*/
if (prev && prev != curr &&
curr->served * prev->cur_eweight > prev->served * curr->cur_eweight)
curr = prev;
if (prev && prev != curr) {
uint64_t wcurr = (uint64_t)curr->served * prev->cur_eweight;
uint64_t wprev = (uint64_t)prev->served * curr->cur_eweight;
if (wcurr > wprev)
curr = prev;
else if (wcurr == wprev && curr->counters.shared.tg && prev->counters.shared.tg) {
/* same load: pick the lowest weighted request rate */
wcurr = read_freq_ctr_period_estimate(&curr->counters._sess_per_sec, MS_TO_TICKS(1000));
wprev = read_freq_ctr_period_estimate(&prev->counters._sess_per_sec, MS_TO_TICKS(1000));
if (wprev * curr->cur_eweight < wcurr * prev->cur_eweight)
curr = prev;
}
}
} while (--draws > 0);
/* if the selected server is full, pretend we have none so that we reach
@ -3044,6 +3056,27 @@ int be_downtime(struct proxy *px) {
return ns_to_sec(now_ns) - px->last_change + px->down_time;
}
/* Checks if <px> backend supports the addition of servers at runtime. Either a
* backend or a defaults proxy are supported. If proxy is incompatible, <msg>
* will be allocated to contain a textual explaination.
*/
int be_supports_dynamic_srv(struct proxy *px, char **msg)
{
if (px->lbprm.algo && !(px->lbprm.algo & BE_LB_PROP_DYN)) {
memprintf(msg, "%s '%s' uses a non dynamic load balancing method",
proxy_cap_str(px->cap), px->id);
return 0;
}
if (px->mode == PR_MODE_SYSLOG) {
memprintf(msg, "%s '%s' uses mode log",
proxy_cap_str(px->cap), px->id);
return 0;
}
return 1;
}
/*
* This function returns a string containing the balancing
* mode of the proxy in a format suitable for stats.

View file

@ -501,84 +501,10 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
curproxy->conf.file_prev = file_prev;
curproxy->conf.line_prev = line_prev;
if (curr_defproxy && (!LIST_ISEMPTY(&curr_defproxy->http_req_rules) ||
!LIST_ISEMPTY(&curr_defproxy->http_res_rules) ||
!LIST_ISEMPTY(&curr_defproxy->http_after_res_rules) ||
!LIST_ISEMPTY(&curr_defproxy->tcp_req.l4_rules) ||
!LIST_ISEMPTY(&curr_defproxy->tcp_req.l5_rules) ||
!LIST_ISEMPTY(&curr_defproxy->tcp_req.inspect_rules) ||
!LIST_ISEMPTY(&curr_defproxy->tcp_rep.inspect_rules))) {
/* If the current default proxy defines TCP/HTTP rules, the
* current proxy will keep a reference on it. But some sanity
* checks are performed first:
*
* - It cannot be used to init a defaults section
* - It cannot be used to init a listen section
* - It cannot be used to init backend and frontend sections at
* same time. It can be used to init several sections of the
* same type only.
* - It cannot define L4/L5 TCP rules if it is used to init
* backend sections.
* - It cannot define 'tcp-response content' rules if it
* is used to init frontend sections.
*
* If no error is found, refcount of the default proxy is incremented.
*/
/* Note: Add tcpcheck_rules too if unresolve args become allowed in defaults section */
if (rc & PR_CAP_DEF) {
ha_alert("parsing [%s:%d]: a defaults section cannot inherit from a defaults section defining TCP/HTTP rules (defaults section at %s:%d).\n",
file, linenum, curr_defproxy->conf.file, curr_defproxy->conf.line);
err_code |= ERR_ALERT | ERR_ABORT;
}
else if ((rc & PR_CAP_LISTEN) == PR_CAP_LISTEN) {
ha_alert("parsing [%s:%d]: a listen section cannot inherit from a defaults section defining TCP/HTTP rules.\n",
file, linenum);
err_code |= ERR_ALERT | ERR_ABORT;
}
else {
char defcap = (curr_defproxy->cap & PR_CAP_LISTEN);
if ((defcap == PR_CAP_BE || defcap == PR_CAP_FE) && (rc & PR_CAP_LISTEN) != defcap) {
ha_alert("parsing [%s:%d]: frontends and backends cannot inherit from the same defaults section"
" if it defines TCP/HTTP rules (defaults section at %s:%d).\n",
file, linenum, curr_defproxy->conf.file, curr_defproxy->conf.line);
err_code |= ERR_ALERT | ERR_ABORT;
}
else if (!(rc & PR_CAP_FE) && (!LIST_ISEMPTY(&curr_defproxy->tcp_req.l4_rules) ||
!LIST_ISEMPTY(&curr_defproxy->tcp_req.l5_rules))) {
ha_alert("parsing [%s:%d]: a backend section cannot inherit from a defaults section defining"
" 'tcp-request connection' or 'tcp-request session' rules (defaults section at %s:%d).\n",
file, linenum, curr_defproxy->conf.file, curr_defproxy->conf.line);
err_code |= ERR_ALERT | ERR_ABORT;
}
else if (!(rc & PR_CAP_BE) && !LIST_ISEMPTY(&curr_defproxy->tcp_rep.inspect_rules)) {
ha_alert("parsing [%s:%d]: a frontend section cannot inherit from a defaults section defining"
" 'tcp-response content' rules (defaults section at %s:%d).\n",
file, linenum, curr_defproxy->conf.file, curr_defproxy->conf.line);
err_code |= ERR_ALERT | ERR_ABORT;
}
else {
curr_defproxy->cap = (curr_defproxy->cap & ~PR_CAP_LISTEN) | (rc & PR_CAP_LISTEN);
proxy_ref_defaults(curproxy, curr_defproxy);
}
}
}
if (curr_defproxy && (curr_defproxy->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) &&
(curproxy->cap & PR_CAP_LISTEN) == PR_CAP_BE) {
/* If the current default proxy defines tcpcheck rules, the
* current proxy will keep a reference on it. but only if the
* current proxy has the backend capability.
*/
proxy_ref_defaults(curproxy, curr_defproxy);
}
if ((rc & PR_CAP_BE) && curr_defproxy && (curr_defproxy->nb_req_cap || curr_defproxy->nb_rsp_cap)) {
ha_alert("parsing [%s:%d]: backend or defaults sections cannot inherit from a defaults section defining"
" capptures (defaults section at %s:%d).\n",
file, linenum, curr_defproxy->conf.file, curr_defproxy->conf.line);
err_code |= ERR_ALERT | ERR_ABORT;
if (curr_defproxy) {
err_code = proxy_ref_defaults(curproxy, curr_defproxy, &errmsg);
if (err_code)
ha_alert("parsing [%s:%d]: %s.\n", file, linenum, errmsg);
}
if (rc & PR_CAP_DEF) {
@ -707,23 +633,32 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
goto out;
}
else if (strcmp(args[0], "mode") == 0) { /* sets the proxy mode */
enum pr_mode mode;
if (alertif_too_many_args(1, file, linenum, args, &err_code))
goto out;
if (strcmp(args[1], "http") == 0) curproxy->mode = PR_MODE_HTTP;
else if (strcmp(args[1], "tcp") == 0) curproxy->mode = PR_MODE_TCP;
else if (strcmp(args[1], "log") == 0 && (curproxy->cap & PR_CAP_BE)) curproxy->mode = PR_MODE_SYSLOG;
else if (strcmp(args[1], "spop") == 0 && (curproxy->cap & PR_CAP_BE)) curproxy->mode = PR_MODE_SPOP;
else if (strcmp(args[1], "health") == 0) {
if (unlikely(strcmp(args[1], "health") == 0)) {
ha_alert("parsing [%s:%d] : 'mode health' doesn't exist anymore. Please use 'http-request return status 200' instead.\n", file, linenum);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
else {
mode = str_to_proxy_mode(args[1]);
if (!mode) {
ha_alert("parsing [%s:%d] : unknown proxy mode '%s'.\n", file, linenum, args[1]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
else if ((mode == PR_MODE_SYSLOG || mode == PR_MODE_SPOP) &&
!(curproxy->cap & PR_CAP_BE)) {
ha_alert("parsing [%s:%d] : mode %s is only applicable on proxies with backend capability.\n", file, linenum, proxy_mode_str(mode));
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
curproxy->mode = mode;
if (curproxy->cap & PR_CAP_DEF)
curproxy->flags |= PR_FL_DEF_EXPLICIT_MODE;
}
else if (strcmp(args[0], "id") == 0) {
struct proxy *conflict;

View file

@ -496,6 +496,36 @@ static int ssl_parse_global_keylog(char **args, int section_type, struct proxy *
}
#endif
/* Allow to explicitely disable certificate compression when set to "off" */
#ifdef SSL_OP_NO_RX_CERTIFICATE_COMPRESSION
static int ssl_parse_certificate_compression(char **args, int section_type, struct proxy *curpx,
const struct proxy *defpx, const char *file, int line,
char **err)
{
if (too_many_args(1, args, err, NULL))
return -1;
if (strcmp(args[1], "auto") == 0)
global_ssl.certificate_compression = 1;
else if (strcmp(args[1], "off") == 0)
global_ssl.certificate_compression = 0;
else {
memprintf(err, "'%s' expects either 'auto' or 'off' but got '%s'.", args[0], args[1]); return -1;
}
return 0;
}
#else
static int ssl_parse_certificate_compression(char **args, int section_type, struct proxy *curpx,
const struct proxy *defpx, const char *file, int line,
char **err)
{
memprintf(err, "'%s' is not supported by your TLS library. "
"It is known to work only with OpenSSL >= 3.2.0.", args[0]);
return -1;
}
#endif
/* parse "ssl.force-private-cache".
* Returns <0 on alert, >0 on warning, 0 on success.
*/
@ -943,7 +973,7 @@ static int ssl_bind_parse_ktls(char **args, int cur_arg, struct proxy *px, struc
return ERR_ALERT | ERR_FATAL;
}
if (!experimental_directives_allowed) {
memprintf(err, "'%s' directive is experimental, must be allowed via a global 'expose-experimental-directive'", args[cur_arg]);
memprintf(err, "'%s' directive is experimental, must be allowed via a global 'expose-experimental-directives'", args[cur_arg]);
return ERR_ALERT | ERR_FATAL;
}
if (!strcasecmp(args[cur_arg + 1], "on")) {
@ -2020,7 +2050,7 @@ static int srv_parse_ktls(char **args, int *cur_arg, struct proxy *px, struct se
}
if (!experimental_directives_allowed) {
memprintf(err, "'%s' directive is experimental, must be allowed via a global 'expose-experimental-directive'", args[*cur_arg]);
memprintf(err, "'%s' directive is experimental, must be allowed via a global 'expose-experimental-directives'", args[*cur_arg]);
return ERR_ALERT | ERR_FATAL;
}
@ -2759,6 +2789,7 @@ static struct cfg_kw_list cfg_kws = {ILH, {
{ CFG_GLOBAL, "ssl-security-level", ssl_parse_security_level },
{ CFG_GLOBAL, "ssl-skip-self-issued-ca", ssl_parse_skip_self_issued_ca },
{ CFG_GLOBAL, "tune.ssl.cachesize", ssl_parse_global_int },
{ CFG_GLOBAL, "tune.ssl.certificate-compression", ssl_parse_certificate_compression },
{ CFG_GLOBAL, "tune.ssl.default-dh-param", ssl_parse_global_default_dh },
{ CFG_GLOBAL, "tune.ssl.force-private-cache", ssl_parse_global_private_cache },
{ CFG_GLOBAL, "tune.ssl.lifetime", ssl_parse_global_lifetime },

File diff suppressed because it is too large Load diff

View file

@ -686,7 +686,10 @@ int _cmp_cluster_avg_capa(const void *a, const void *b)
{
const struct ha_cpu_cluster *l = (const struct ha_cpu_cluster *)a;
const struct ha_cpu_cluster *r = (const struct ha_cpu_cluster *)b;
return r->capa - l->capa;
if (!r->nb_cores || !l->nb_cores)
return r->nb_cores - l->nb_cores;
return r->capa * l->nb_cores - l->capa * r->nb_cores;
}
/* re-order a cluster array by cluster index only */
@ -1669,7 +1672,7 @@ static int cpu_policy_performance(int policy, int tmin, int tmax, int gmin, int
capa = 0;
for (cluster = 0; cluster < cpu_topo_maxcpus; cluster++) {
if (capa && ha_cpu_clusters[cluster].capa * 10 < ha_cpu_clusters[cluster].nb_cpu * capa * 8) {
if (capa && ha_cpu_clusters[cluster].capa * 10 < ha_cpu_clusters[cluster].nb_cores * capa * 8) {
/* This cluster is made of cores delivering less than
* 80% of the performance of those of the previous
* cluster, previous one, we're not interested in
@ -1680,8 +1683,8 @@ static int cpu_policy_performance(int policy, int tmin, int tmax, int gmin, int
ha_cpu_topo[cpu].st |= HA_CPU_F_IGNORED;
}
}
else if (ha_cpu_clusters[cluster].nb_cpu)
capa = ha_cpu_clusters[cluster].capa / ha_cpu_clusters[cluster].nb_cpu;
else if (ha_cpu_clusters[cluster].nb_cores)
capa = ha_cpu_clusters[cluster].capa / ha_cpu_clusters[cluster].nb_cores;
else
capa = 0;
}
@ -1714,7 +1717,7 @@ static int cpu_policy_efficiency(int policy, int tmin, int tmax, int gmin, int g
capa = 0;
for (cluster = cpu_topo_maxcpus - 1; cluster >= 0; cluster--) {
if (capa && ha_cpu_clusters[cluster].capa * 8 >= ha_cpu_clusters[cluster].nb_cpu * capa * 10) {
if (capa && ha_cpu_clusters[cluster].capa * 8 >= ha_cpu_clusters[cluster].nb_cores * capa * 10) {
/* This cluster is made of cores each at last 25% faster
* than those of the previous cluster, previous one, we're
* not interested in using it.
@ -1724,8 +1727,8 @@ static int cpu_policy_efficiency(int policy, int tmin, int tmax, int gmin, int g
ha_cpu_topo[cpu].st |= HA_CPU_F_IGNORED;
}
}
else if (ha_cpu_clusters[cluster].nb_cpu)
capa = ha_cpu_clusters[cluster].capa / ha_cpu_clusters[cluster].nb_cpu;
else if (ha_cpu_clusters[cluster].nb_cores)
capa = ha_cpu_clusters[cluster].capa / ha_cpu_clusters[cluster].nb_cores;
else
capa = 0;
}

View file

@ -724,14 +724,42 @@ static size_t h1_parse_full_contig_chunks(struct h1m *h1m, struct htx **dsthtx,
break;
}
else if (likely(end[ridx] == ';')) {
int backslash = 0;
int quote = 0;
/* chunk extension, ends at next CRLF */
if (!++ridx)
goto end_parsing;
while (!HTTP_IS_CRLF(end[ridx])) {
/* The loop seeks the first CRLF or non-tab CTL char
* and stops there. If a backslash/quote is active,
* it's an error. If none, we assume it's the CRLF
* and go back to the top of the loop checking for
* CR then LF. This way CTLs, lone LF etc are handled
* in the fallback path. This allows to protect
* remotes against their own possibly non-compliant
* chunk-ext parser which could mistakenly skip a
* quoted CRLF. Chunk-ext are not used anyway, except
* by attacks.
*/
while (!HTTP_IS_CTL(end[ridx]) || HTTP_IS_SPHT(end[ridx])) {
if (backslash)
backslash = 0; // escaped char
else if (end[ridx] == '\\' && quote)
backslash = 1;
else if (end[ridx] == '\\') // backslash not permitted outside quotes
goto parsing_error;
else if (end[ridx] == '"') // begin/end of quoted-pair
quote = !quote;
if (!++ridx)
goto end_parsing;
}
/* we have a CRLF now, loop above */
/* mismatched quotes / backslashes end here */
if (quote || backslash)
goto parsing_error;
/* CTLs (CRLF) fall to the common check */
continue;
}
else {

View file

@ -205,6 +205,7 @@ struct global global = {
#endif
/* by default allow clients which use a privileged port for TCP only */
.clt_privileged_ports = HA_PROTO_TCP,
.maxthrpertgroup = MAX_THREADS_PER_GROUP,
/* others NULL OK */
};
@ -1435,11 +1436,15 @@ static void init_early(int argc, char **argv)
len = strlen(progname);
progname = strdup(progname);
if (!progname) {
ha_alert("Cannot allocate memory for log_tag.\n");
ha_alert("Cannot allocate memory for progname.\n");
exit(EXIT_FAILURE);
}
chunk_initlen(&global.log_tag, strdup(progname), len, len);
if (b_orig(&global.log_tag) == NULL) {
ha_alert("Cannot allocate memory for log_tag.\n");
exit(EXIT_FAILURE);
}
}
/* handles program arguments. Very minimal parsing is performed, variables are
@ -2918,9 +2923,11 @@ void run_poll_loop()
if (thread_has_tasks())
activity[tid].wake_tasks++;
else {
_HA_ATOMIC_OR(&th_ctx->flags, TH_FL_SLEEPING);
_HA_ATOMIC_AND(&th_ctx->flags, ~TH_FL_NOTIFIED);
__ha_barrier_atomic_store();
unsigned int flags = _HA_ATOMIC_LOAD(&th_ctx->flags);
while (unlikely(!HA_ATOMIC_CAS(&th_ctx->flags, &flags, (flags | TH_FL_SLEEPING) & ~TH_FL_NOTIFIED)))
__ha_cpu_relax();
if (thread_has_tasks()) {
activity[tid].wake_tasks++;
_HA_ATOMIC_AND(&th_ctx->flags, ~TH_FL_SLEEPING);

View file

@ -273,6 +273,7 @@ static const char *hlua_tostring_safe(lua_State *L, int index)
break;
default:
/* error was caught */
lua_pop(L, 1); // consume the lua object pushed on the stack since we ignore it
return NULL;
}
return str;
@ -323,6 +324,7 @@ static const char *hlua_pushvfstring_safe(lua_State *L, const char *fmt, va_list
break;
default:
/* error was caught */
lua_pop(L, 1); // consume the lua object pushed on the stack since we ignore it
dst = NULL;
}
va_end(cpy_argp);
@ -870,6 +872,7 @@ void hlua_unref(lua_State *L, int ref)
__LJMP static int _hlua_traceback(lua_State *L)
{
lua_Debug *ar = lua_touserdata(L, 1);
int ret;
/* Fill fields:
* 'S': fills in the fields source, short_src, linedefined, lastlinedefined, and what;
@ -877,7 +880,10 @@ __LJMP static int _hlua_traceback(lua_State *L)
* 'n': fills in the field name and namewhat;
* 't': fills in the field istailcall;
*/
return lua_getinfo(L, "Slnt", ar);
ret = lua_getinfo(L, "Slnt", ar);
if (!ret)
WILL_LJMP(luaL_error(L, "unexpected"));
return 0;
}
@ -896,10 +902,11 @@ const char *hlua_traceback(lua_State *L, const char* sep)
lua_pushlightuserdata(L, &ar);
/* safe getinfo */
switch (lua_pcall(L, 1, 1, 0)) {
switch (lua_pcall(L, 1, 0, 0)) {
case LUA_OK:
break;
default:
lua_pop(L, 1); // consume the lua object pushed on the stack since we ignore it
goto end; // abort
}
@ -998,6 +1005,7 @@ static int hlua_pusherror(lua_State *L, const char *fmt, ...)
case LUA_OK:
break;
default:
lua_pop(L, 1); // consume the lua object pushed on the stack since we ignore it
ret = 0;
}
@ -10188,6 +10196,7 @@ static int hlua_new_event_sub_safe(lua_State *L, struct event_hdl_sub *sub)
return 1;
default:
/* error was caught */
lua_pop(L, 1); // consume the lua object pushed on the stack since we ignore it
return 0;
}
}

View file

@ -960,6 +960,12 @@ static enum act_parse_ret parse_http_req_capture(const char **args, int *orig_ar
hdr->namelen = 0;
hdr->len = len;
hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
if (!hdr->pool) {
memprintf(err, "out of memory");
free(hdr);
release_sample_expr(expr);
return ACT_RET_PRS_ERR;
}
hdr->index = px->nb_req_cap++;
px->req_cap = hdr;

View file

@ -552,6 +552,32 @@ struct server *chash_get_next_server(struct proxy *p, struct server *srvtoavoid)
return srv;
}
/* Allocates and initializes lb nodes for server <srv>. Returns < 0 on error.
* This is called by chash_init_server_tree() as well as from srv_alloc_lb()
* for runtime addition.
*/
int chash_server_init(struct server *srv)
{
int node;
srv->lb_nodes = calloc(srv->lb_nodes_tot, sizeof(*srv->lb_nodes));
if (!srv->lb_nodes)
return -1;
srv->lb_server_key = chash_compute_server_key(srv);
for (node = 0; node < srv->lb_nodes_tot; node++) {
srv->lb_nodes[node].server = srv;
srv->lb_nodes[node].node.key = chash_compute_node_key(srv, node);
}
return 0;
}
/* Releases the allocated lb_nodes for this server */
void chash_server_deinit(struct server *srv)
{
ha_free(&srv->lb_nodes);
}
/* This function is responsible for building the active and backup trees for
* consistent hashing. The servers receive an array of initialized nodes
* with their assigned keys. It also sets p->lbprm.wdiv to the eweight to
@ -562,11 +588,12 @@ int chash_init_server_tree(struct proxy *p)
{
struct server *srv;
struct eb_root init_head = EB_ROOT;
int node;
p->lbprm.set_server_status_up = chash_set_server_status_up;
p->lbprm.set_server_status_down = chash_set_server_status_down;
p->lbprm.update_server_eweight = chash_update_server_weight;
p->lbprm.server_init = chash_server_init;
p->lbprm.server_deinit = chash_server_deinit;
p->lbprm.server_take_conn = NULL;
p->lbprm.server_drop_conn = NULL;
@ -588,17 +615,11 @@ int chash_init_server_tree(struct proxy *p)
srv->lb_tree = (srv->flags & SRV_F_BACKUP) ? &p->lbprm.chash.bck : &p->lbprm.chash.act;
srv->lb_nodes_tot = srv->uweight * BE_WEIGHT_SCALE;
srv->lb_nodes_now = 0;
srv->lb_nodes = calloc(srv->lb_nodes_tot,
sizeof(*srv->lb_nodes));
if (!srv->lb_nodes) {
if (chash_server_init(srv) < 0) {
ha_alert("failed to allocate lb_nodes for server %s.\n", srv->id);
return -1;
}
srv->lb_server_key = chash_compute_server_key(srv);
for (node = 0; node < srv->lb_nodes_tot; node++) {
srv->lb_nodes[node].server = srv;
srv->lb_nodes[node].node.key = chash_compute_node_key(srv, node);
}
if (srv_currently_usable(srv))
chash_queue_dequeue_srv(srv);

View file

@ -2488,8 +2488,10 @@ static size_t h1_make_reqline(struct h1s *h1s, struct h1m *h1m, struct htx *htx,
goto end;
type = htx_get_blk_type(blk);
sz = htx_get_blksz(blk);
if (type == HTX_BLK_UNUSED)
if (type == HTX_BLK_UNUSED) {
htx_remove_blk(htx, blk);
continue;
}
if (type != HTX_BLK_REQ_SL || sz > count)
goto error;
break;
@ -2577,8 +2579,10 @@ static size_t h1_make_stline(struct h1s *h1s, struct h1m *h1m, struct htx *htx,
type = htx_get_blk_type(blk);
sz = htx_get_blksz(blk);
if (type == HTX_BLK_UNUSED)
if (type == HTX_BLK_UNUSED) {
htx_remove_blk(htx, blk);
continue;
}
if (type != HTX_BLK_RES_SL || sz > count)
goto error;
break;

View file

@ -652,8 +652,8 @@ static int sample_conv_tcp_win(const struct arg *arg_p, struct sample *smp, void
/* Builds a binary fingerprint of the IP+TCP input contents that are supposed
* to rely essentially on the client stack's settings. This can be used for
* example to selectively block bad behaviors at one IP address without
* blocking others. The resulting fingerprint is a binary block of 56 to 376
* bytes long (56 being the fixed part and the rest depending on the provided
* blocking others. The resulting fingerprint is a binary block of 64 to 384
* bits long (64 being the fixed part and the rest depending on the provided
* TCP extensions).
*/
static int sample_conv_ip_fp(const struct arg *arg_p, struct sample *smp, void *private)
@ -668,6 +668,7 @@ static int sample_conv_ip_fp(const struct arg *arg_p, struct sample *smp, void *
uchar tcpflags;
uchar tcplen;
uchar tcpws;
uchar opts;
ushort pktlen;
ushort tcpwin;
ushort tcpmss;
@ -719,8 +720,8 @@ static int sample_conv_ip_fp(const struct arg *arg_p, struct sample *smp, void *
else
return 0;
/* prepare trash to contain at least 7 bytes */
trash->data = 7;
/* prepare trash to contain at least 8 bytes */
trash->data = 8;
/* store the TOS in the FP's first byte */
trash->area[0] = iptos;
@ -763,9 +764,11 @@ static int sample_conv_ip_fp(const struct arg *arg_p, struct sample *smp, void *
(tcpflags >> 6 << 0); // CWR, ECE
tcpmss = tcpws = 0;
opts = 0;
ofs = 20;
while (ofs < tcplen) {
size_t next;
uchar opt;
if (smp->data.u.str.area[ofs] == 0) // kind0=end of options
break;
@ -782,17 +785,24 @@ static int sample_conv_ip_fp(const struct arg *arg_p, struct sample *smp, void *
break;
/* option is complete, take a copy of it */
if (mode & 2) // mode & 2: append tcp.options_list
trash->area[trash->data++] = smp->data.u.str.area[ofs];
opt = smp->data.u.str.area[ofs];
if (smp->data.u.str.area[ofs] == 2 /* MSS */) {
if (mode & 2) // mode & 2: append tcp.options_list
trash->area[trash->data++] = opt;
if (opt == 2 /* MSS */) {
tcpmss = read_n16(smp->data.u.str.area + ofs + 2);
}
else if (smp->data.u.str.area[ofs] == 3 /* WS */) {
else if (opt == 3 /* WS */) {
tcpws = (uchar)smp->data.u.str.area[ofs + 2];
/* output from 1 to 15, thus 0=not found */
tcpws = tcpws > 14 ? 15 : tcpws + 1;
}
/* keep a presence mask of opts 2..8 and others */
if (opt >= 2)
opts |= 1 << (opt < 9 ? opt - 2 : 7);
ofs = next;
}
@ -803,6 +813,9 @@ static int sample_conv_ip_fp(const struct arg *arg_p, struct sample *smp, void *
write_n16(trash->area + 3, tcpwin);
write_n16(trash->area + 5, tcpmss);
/* the the bit mask of present options */
trash->area[7] = opts;
/* mode 4: append source IP address */
if (mode & 4) {
iplen = (ipver == 4) ? 4 : 16;

View file

@ -302,7 +302,7 @@ static int mem_should_fail(const struct pool_head *pool)
* registration struct. Use create_pool() instead which does it for free.
* The alignment will be stored as-is in the registration.
*/
struct pool_head *create_pool_with_loc(const char *name, unsigned int size,
struct pool_head *create_pool_with_loc(const char *name, ullong size,
unsigned int align, unsigned int flags,
const char *file, unsigned int line)
{
@ -335,7 +335,8 @@ struct pool_head *create_pool_from_reg(const char *name, struct pool_registratio
{
unsigned int extra_mark, extra_caller, extra;
unsigned int flags = reg->flags;
unsigned int size = reg->size;
ullong reg_size = reg->size; // copy of the originally requested size
ullong size = reg_size;
unsigned int alignment = reg->align;
struct pool_head *pool = NULL;
struct pool_head *entry;
@ -374,6 +375,9 @@ struct pool_head *create_pool_from_reg(const char *name, struct pool_registratio
extra_caller = (pool_debugging & POOL_DBG_CALLER) ? POOL_EXTRA_CALLER : 0;
extra = extra_mark + extra_caller;
if (size > 0xFFFFFFFFULL || (size + extra) > 0xFFFFFFFFULL || (uint)(size + extra) < (uint)reg_size)
goto ovf;
if (!(pool_debugging & POOL_DBG_NO_CACHE)) {
/* we'll store two lists there, we need the room for this. Let's
* make sure it's always OK even when including the extra word
@ -392,7 +396,7 @@ struct pool_head *create_pool_from_reg(const char *name, struct pool_registratio
*/
if (!(flags & MEM_F_EXACT)) {
align = (pool_debugging & POOL_DBG_TAG) ? sizeof(void *) : 16;
size = ((size + align - 1) & -align);
size = ((size + align - 1) & -(ullong)align);
}
if (pool_debugging & POOL_DBG_BACKUP) {
@ -402,6 +406,9 @@ struct pool_head *create_pool_from_reg(const char *name, struct pool_registratio
extra += size;
}
if (size > 0xFFFFFFFFULL || (size + extra) > 0xFFFFFFFFULL || (uint)(size + extra) < (uint)reg_size)
goto ovf;
/* TODO: thread: we do not lock pool list for now because all pools are
* created during HAProxy startup (so before threads creation) */
start = &pools;
@ -496,6 +503,11 @@ struct pool_head *create_pool_from_reg(const char *name, struct pool_registratio
fail:
return pool;
ovf:
ha_alert("Failed to create pool '%s' of size '%llu': overflow detected due to too large "
"a configured size and/or configured pool options. Aborting.\n",
name, reg_size);
return NULL;
}
/* Tries to allocate an object for the pool <pool> using the system's allocator
@ -794,7 +806,8 @@ void pool_put_to_cache(struct pool_head *pool, void *ptr, const void *caller)
if (unlikely(pool_cache_bytes > global.tune.pool_cache_size * 3 / 4)) {
uint64_t mem_wait_start = 0;
if (unlikely(th_ctx->flags & TH_FL_TASK_PROFILING))
if (unlikely((th_ctx->flags & (TH_FL_TASK_PROFILING|TH_FL_TASK_PROFILING_M)) ==
(TH_FL_TASK_PROFILING|TH_FL_TASK_PROFILING_M)))
mem_wait_start = now_mono_time();
if (ph->count >= 16 + pool_cache_count / 8 + CONFIG_HAP_POOL_CLUSTER_SIZE)
@ -957,7 +970,8 @@ void pool_gc(struct pool_head *pool_ctx)
uint64_t mem_wait_start = 0;
int isolated = thread_isolated();
if (unlikely(th_ctx->flags & TH_FL_TASK_PROFILING))
if (unlikely((th_ctx->flags & (TH_FL_TASK_PROFILING|TH_FL_TASK_PROFILING_M)) ==
(TH_FL_TASK_PROFILING|TH_FL_TASK_PROFILING_M)))
mem_wait_start = now_mono_time();
if (!isolated)
@ -1019,7 +1033,8 @@ void *__pool_alloc(struct pool_head *pool, unsigned int flags)
/* count allocation time only for cache misses */
uint64_t mem_wait_start = 0;
if (unlikely(th_ctx->flags & TH_FL_TASK_PROFILING))
if (unlikely((th_ctx->flags & (TH_FL_TASK_PROFILING|TH_FL_TASK_PROFILING_M)) ==
(TH_FL_TASK_PROFILING|TH_FL_TASK_PROFILING_M)))
mem_wait_start = now_mono_time();
p = pool_alloc_nocache(pool, caller);
@ -1097,7 +1112,8 @@ void __pool_free(struct pool_head *pool, void *ptr)
global.tune.pool_cache_size < pool->size)) {
uint64_t mem_wait_start = 0;
if (unlikely(th_ctx->flags & TH_FL_TASK_PROFILING))
if (unlikely((th_ctx->flags & (TH_FL_TASK_PROFILING|TH_FL_TASK_PROFILING_M)) ==
(TH_FL_TASK_PROFILING|TH_FL_TASK_PROFILING_M)))
mem_wait_start = now_mono_time();
pool_free_nocache(pool, ptr);
@ -1428,7 +1444,7 @@ int dump_pools_info(struct appctx *appctx)
struct pool_registration *reg;
list_for_each_entry(reg, &ctx->pool_info[i].entry->regs, list) {
chunk_appendf(&trash, " > %-12s: size=%u flags=%#x align=%u", reg->name, reg->size, reg->flags, reg->align);
chunk_appendf(&trash, " > %-12s: size=%llu flags=%#x align=%u", reg->name, reg->size, reg->flags, reg->align);
if (reg->file && reg->line)
chunk_appendf(&trash, " [%s:%u]", reg->file, reg->line);
chunk_appendf(&trash, "\n");
@ -1651,7 +1667,7 @@ void create_pool_callback(struct pool_head **ptr, char *name, struct pool_regist
{
*ptr = create_pool_from_reg(name, reg);
if (!*ptr) {
ha_alert("Failed to allocate pool '%s' of size %u : %s. Aborting.\n",
ha_alert("Failed to allocate pool '%s' of size %llu : %s. Aborting.\n",
name, reg->size, strerror(errno));
exit(1);
}

View file

@ -1028,7 +1028,7 @@ static int tcp_get_info(struct connection *conn, long long int *info, int info_n
static void __proto_tcp_init(void)
{
#if defined(__linux__) && !defined(TCP_MD5SIG)
#if defined(__linux__) && defined(TCP_MD5SIG)
hap_register_feature("HAVE_TCP_MD5SIG");
#endif
}

File diff suppressed because it is too large Load diff

View file

@ -359,8 +359,7 @@ leave:
}
/* Callback use to parse TLS messages for <ssl> TLS session. */
void quic_tls_compat_msg_callback(struct connection *conn,
int write_p, int version, int content_type,
void quic_tls_compat_msg_callback(int write_p, int version, int content_type,
const void *buf, size_t len, SSL *ssl)
{
unsigned int alert;

View file

@ -3184,7 +3184,6 @@ void srv_free_params(struct server *srv)
free(srv->cc_algo);
free(srv->tcp_md5sig);
free(srv->addr_key);
free(srv->lb_nodes);
counters_be_shared_drop(&srv->counters.shared);
if (srv->log_target) {
deinit_log_target(srv->log_target);
@ -5889,25 +5888,13 @@ static int cli_parse_enable_server(char **args, char *payload, struct appctx *ap
*/
static int srv_alloc_lb(struct server *sv, struct proxy *be)
{
int node;
sv->lb_tree = (sv->flags & SRV_F_BACKUP) ?
&be->lbprm.chash.bck : &be->lbprm.chash.act;
sv->lb_nodes_tot = sv->uweight * BE_WEIGHT_SCALE;
sv->lb_nodes_now = 0;
if (((be->lbprm.algo & (BE_LB_KIND | BE_LB_PARM)) == (BE_LB_KIND_RR | BE_LB_RR_RANDOM)) ||
((be->lbprm.algo & (BE_LB_KIND | BE_LB_HASH_TYPE)) == (BE_LB_KIND_HI | BE_LB_HASH_CONS))) {
sv->lb_nodes = calloc(sv->lb_nodes_tot, sizeof(*sv->lb_nodes));
if (!sv->lb_nodes)
return 0;
for (node = 0; node < sv->lb_nodes_tot; node++) {
sv->lb_nodes[node].server = sv;
sv->lb_nodes[node].node.key = full_hash(sv->puid * SRV_EWGHT_RANGE + node);
}
}
if (be->lbprm.server_init && be->lbprm.server_init(sv) < 0)
return 0; // typically out of memory
return 1;
}
@ -6104,7 +6091,7 @@ static int cli_parse_add_server(char **args, char *payload, struct appctx *appct
struct add_srv_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
struct proxy *be;
struct server *srv;
char *be_name, *sv_name;
char *be_name, *sv_name, *errmsg;
int errcode, argc;
int next_id;
const int parse_flags = SRV_PARSE_DYNAMIC|SRV_PARSE_PARSE_ADDR;
@ -6140,13 +6127,9 @@ static int cli_parse_add_server(char **args, char *payload, struct appctx *appct
if (!be)
return cli_err(appctx, "No such backend.\n");
if (!(be->lbprm.algo & BE_LB_PROP_DYN)) {
cli_err(appctx, "Backend must use a dynamic load balancing to support dynamic servers.\n");
return 1;
}
if (be->mode == PR_MODE_SYSLOG) {
cli_err(appctx," Dynamic servers cannot be used with log backends.\n");
errmsg = NULL;
if (!be_supports_dynamic_srv(be, &errmsg)) {
cli_dynerr(appctx, memprintf(&errmsg, "Backend does not support dynamic servers : %s.\n", errmsg));
return 1;
}
@ -7416,10 +7399,9 @@ int srv_add_to_idle_list(struct server *srv, struct connection *conn, int is_saf
HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
_HA_ATOMIC_INC(&srv->curr_idle_thr[tid]);
__ha_barrier_full();
if ((volatile void *)srv->idle_node.node.leaf_p == NULL) {
if (HA_ATOMIC_LOAD(&srv->idle_node.node.leaf_p) == NULL) {
HA_SPIN_LOCK(OTHER_LOCK, &idle_conn_srv_lock);
if ((volatile void *)srv->idle_node.node.leaf_p == NULL) {
if (_HA_ATOMIC_LOAD(&srv->idle_node.node.leaf_p) == NULL) {
srv->idle_node.key = tick_add(srv->pool_purge_delay,
now_ms);
eb32_insert(&idle_conn_srv, &srv->idle_node);

View file

@ -593,7 +593,7 @@ int ssl_sock_load_key_into_ckch(const char *path, char *buf, struct ckch_data *d
BIO *in = NULL;
int ret = 1;
EVP_PKEY *key = NULL;
struct passphrase_cb_data cb_data = { path, data, 0 };
struct passphrase_cb_data cb_data = { path, data, 0, 0 };
if (buf) {
/* reading from a buffer */
@ -625,11 +625,13 @@ int ssl_sock_load_key_into_ckch(const char *path, char *buf, struct ckch_data *d
*/
do {
key = PEM_read_bio_PrivateKey(in, NULL, ssl_sock_passwd_cb, &cb_data);
} while (!key && cb_data.passphrase_idx != -1);
} while (!key && cb_data.passphrase_idx != -1 && cb_data.callback_called);
if (key == NULL) {
memprintf(err, "%sunable to load private key from file '%s'.\n",
err && *err ? *err : "", path);
unsigned long e = ERR_peek_last_error();
memprintf(err, "%sunable to load private key from file '%s' (%s).\n",
err && *err ? *err : "", path, ERR_error_string(e, NULL));
goto end;
}
@ -667,6 +669,7 @@ int ssl_sock_load_pem_into_ckch(const char *path, char *buf, struct ckch_data *d
HASSL_DH *dh = NULL;
STACK_OF(X509) *chain = NULL;
struct issuer_chain *issuer_chain = NULL;
struct passphrase_cb_data cb_data = { path, data, 0, 0 };
if (buf) {
/* reading from a buffer */
@ -691,8 +694,18 @@ int ssl_sock_load_pem_into_ckch(const char *path, char *buf, struct ckch_data *d
}
}
/* Read Private Key */
key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
/* Read Private Key
* Since multiple private keys might have different passphrases that are
* stored in a local cache, we want to try all the already known
* passphrases first before raising an error. The passphrase_idx field
* of the cb_data parameter will be modified in the callback and set to
* -1 after the external passphrase tool is called.
*/
/* We don't know yet if the private key requires a password. */
data->encrypted_privkey = 0;
do {
key = PEM_read_bio_PrivateKey(in, NULL, ssl_sock_passwd_cb, &cb_data);
} while (!key && cb_data.passphrase_idx != -1 && cb_data.callback_called);
/* no need to check for errors here, because the private key could be loaded later */
#ifndef OPENSSL_NO_DH
@ -1332,7 +1345,7 @@ struct cafile_entry *ssl_store_dup_cafile_entry(struct cafile_entry *src)
{
struct cafile_entry *dst = NULL;
X509_STORE *store = NULL;
STACK_OF(X509_OBJECT) *objs;
STACK_OF(X509_OBJECT) *objs = NULL;
int i;
if (!src)
@ -1344,7 +1357,7 @@ struct cafile_entry *ssl_store_dup_cafile_entry(struct cafile_entry *src)
if (!store)
goto err;
objs = X509_STORE_get0_objects(src->ca_store);
objs = X509_STORE_getX_objects(src->ca_store);
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
X509 *cert;
X509_CRL *crl;
@ -1372,10 +1385,11 @@ struct cafile_entry *ssl_store_dup_cafile_entry(struct cafile_entry *src)
}
}
dst = ssl_store_create_cafile_entry(src->path, store, src->type);
sk_X509_OBJECT_popX_free(objs, X509_OBJECT_free);
return dst;
err:
sk_X509_OBJECT_popX_free(objs, X509_OBJECT_free);
X509_STORE_free(store);
ha_free(&dst);
@ -1476,6 +1490,25 @@ end:
return retval;
}
/*
* return the default verify cert directory.
*
* It might provided by the SSL library or set in an environment variable
* (commonly SSL_CERT_DIR)
*/
const char *ha_default_cert_dir()
{
const char *dir = NULL;
const char *certdir_varname = X509_get_default_cert_dir_env();
if (certdir_varname)
dir = getenv(certdir_varname);
if (dir == NULL)
dir = X509_get_default_cert_dir();
return dir;
}
/*
* Try to load a ca-file from disk into the ca-file cache.
* <shuterror> allows you to to stop emitting the errors.
@ -1483,13 +1516,13 @@ end:
*/
int __ssl_store_load_locations_file(char *path, int create_if_none, enum cafile_type type, int shuterror)
{
STACK_OF(X509_OBJECT) *objs = NULL;
X509_STORE *store = ssl_store_get0_locations_file(path);
/* If this function is called by the CLI, we should not call the
* X509_STORE_load_locations function because it performs forbidden disk
* accesses. */
if (!store && create_if_none) {
STACK_OF(X509_OBJECT) *objs;
int cert_count = 0;
struct stat buf;
struct cafile_entry *ca_e;
@ -1505,7 +1538,7 @@ int __ssl_store_load_locations_file(char *path, int create_if_none, enum cafile_
}
if (strcmp(path, "@system-ca") == 0) {
dir = X509_get_default_cert_dir();
dir = ha_default_cert_dir();
if (!dir) {
if (!shuterror)
ha_alert("Couldn't get the system CA directory from X509_get_default_cert_dir().\n");
@ -1594,7 +1627,7 @@ scandir_err:
}
}
objs = X509_STORE_get0_objects(store);
objs = X509_STORE_getX_objects(store);
cert_count = sk_X509_OBJECT_num(objs);
if (cert_count == 0) {
if (!shuterror)
@ -1608,9 +1641,11 @@ scandir_err:
}
ebst_insert(&cafile_tree, &ca_e->node);
}
sk_X509_OBJECT_popX_free(objs, X509_OBJECT_free);
return (store != NULL);
err:
sk_X509_OBJECT_popX_free(objs, X509_OBJECT_free);
X509_STORE_free(store);
store = NULL;
return 0;
@ -3781,7 +3816,7 @@ static int cli_io_handler_show_cafile_detail(struct appctx *appctx)
struct buffer *out = alloc_trash_chunk();
int i = 0;
X509 *cert;
STACK_OF(X509_OBJECT) *objs;
STACK_OF(X509_OBJECT) *objs = NULL;
int retval = 0;
int ca_index = ctx->ca_index;
int show_all = ctx->show_all;
@ -3807,7 +3842,7 @@ static int cli_io_handler_show_cafile_detail(struct appctx *appctx)
if (!cafile_entry->ca_store)
goto end;
objs = X509_STORE_get0_objects(cafile_entry->ca_store);
objs = X509_STORE_getX_objects(cafile_entry->ca_store);
for (i = ca_index; i < sk_X509_OBJECT_num(objs); i++) {
cert = X509_OBJECT_get0_X509(sk_X509_OBJECT_value(objs, i));
@ -3830,13 +3865,16 @@ static int cli_io_handler_show_cafile_detail(struct appctx *appctx)
}
end:
sk_X509_OBJECT_popX_free(objs, X509_OBJECT_free);
free_trash_chunk(out);
return 1; /* end, don't come back */
end_no_putchk:
sk_X509_OBJECT_popX_free(objs, X509_OBJECT_free);
free_trash_chunk(out);
return 1;
yield:
sk_X509_OBJECT_popX_free(objs, X509_OBJECT_free);
/* save the current state */
ctx->ca_index = i;
free_trash_chunk(out);
@ -3939,9 +3977,10 @@ static int get_certificate_count(struct cafile_entry *cafile_entry)
STACK_OF(X509_OBJECT) *objs;
if (cafile_entry && cafile_entry->ca_store) {
objs = X509_STORE_get0_objects(cafile_entry->ca_store);
objs = X509_STORE_getX_objects(cafile_entry->ca_store);
if (objs)
cert_count = sk_X509_OBJECT_num(objs);
sk_X509_OBJECT_popX_free(objs, X509_OBJECT_free);
}
return cert_count;
}
@ -4471,7 +4510,7 @@ static int cli_io_handler_show_crlfile_detail(struct appctx *appctx)
struct buffer *out = alloc_trash_chunk();
int i;
X509_CRL *crl;
STACK_OF(X509_OBJECT) *objs;
STACK_OF(X509_OBJECT) *objs = NULL;
int retval = 0;
int index = ctx->index;
@ -4496,7 +4535,7 @@ static int cli_io_handler_show_crlfile_detail(struct appctx *appctx)
if (!cafile_entry->ca_store)
goto end;
objs = X509_STORE_get0_objects(cafile_entry->ca_store);
objs = X509_STORE_getX_objects(cafile_entry->ca_store);
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
crl = X509_OBJECT_get0_X509_CRL(sk_X509_OBJECT_value(objs, i));
if (!crl)
@ -4519,9 +4558,11 @@ end:
goto yield;
end_no_putchk:
sk_X509_OBJECT_popX_free(objs, X509_OBJECT_free);
free_trash_chunk(out);
return 1;
yield:
sk_X509_OBJECT_popX_free(objs, X509_OBJECT_free);
free_trash_chunk(out);
return 0; /* should come back */
}

View file

@ -152,6 +152,9 @@ struct global_ssl global_ssl = {
#endif
#ifdef HAVE_ACME
.acme_scheduler = 1,
#endif
#ifdef SSL_OP_NO_RX_CERTIFICATE_COMPRESSION
.certificate_compression = 1,
#endif
.renegotiate = SSL_RENEGOTIATE_DFLT,
.passphrase_cmd = NULL,
@ -627,7 +630,7 @@ static int ssl_set_cert_crl_file(X509_STORE *store_ctx, char *path)
if (store_ctx && store) {
int i;
X509_OBJECT *obj;
STACK_OF(X509_OBJECT) *objs = X509_STORE_get0_objects(store);
STACK_OF(X509_OBJECT) *objs = X509_STORE_getX_objects(store);
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
obj = sk_X509_OBJECT_value(objs, i);
switch (X509_OBJECT_get_type(obj)) {
@ -641,6 +644,7 @@ static int ssl_set_cert_crl_file(X509_STORE *store_ctx, char *path)
break;
}
}
sk_X509_OBJECT_popX_free(objs, X509_OBJECT_free);
return 1;
}
return 0;
@ -684,7 +688,7 @@ static STACK_OF(X509_NAME)* ssl_get_client_ca_file(char *path)
skn = sk_X509_NAME_new_null();
/* take x509 from cafile_tree */
objs = X509_STORE_get0_objects(ca_e->ca_store);
objs = X509_STORE_getX_objects(ca_e->ca_store);
for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
x = X509_OBJECT_get0_X509(sk_X509_OBJECT_value(objs, i));
if (!x)
@ -720,6 +724,7 @@ static STACK_OF(X509_NAME)* ssl_get_client_ca_file(char *path)
ca_name->xname = xn;
eb64_insert(&ca_name_tree, &ca_name->node);
}
sk_X509_OBJECT_popX_free(objs, X509_OBJECT_free);
ca_e->ca_list = skn;
/* remove temporary ca_name tree */
node = eb64_first(&ca_name_tree);
@ -796,16 +801,16 @@ static struct eb_root *sh_ssl_sess_tree; /* ssl shared session tree */
/* Dedicated callback functions for heartbeat and clienthello.
*/
#ifdef TLS1_RT_HEARTBEAT
static void ssl_sock_parse_heartbeat(struct connection *conn, int write_p, int version,
static void ssl_sock_parse_heartbeat(int write_p, int version,
int content_type, const void *buf, size_t len,
SSL *ssl);
#endif
static void ssl_sock_parse_clienthello(struct connection *conn, int write_p, int version,
static void ssl_sock_parse_clienthello(int write_p, int version,
int content_type, const void *buf, size_t len,
SSL *ssl);
#ifdef HAVE_SSL_KEYLOG
static void ssl_init_keylog(struct connection *conn, int write_p, int version,
static void ssl_init_keylog(int write_p, int version,
int content_type, const void *buf, size_t len,
SSL *ssl);
#endif
@ -1796,18 +1801,23 @@ int ssl_sock_bind_verifycbk(int ok, X509_STORE_CTX *x_store)
}
#ifdef TLS1_RT_HEARTBEAT
static void ssl_sock_parse_heartbeat(struct connection *conn, int write_p, int version,
static void ssl_sock_parse_heartbeat(int write_p, int version,
int content_type, const void *buf, size_t len,
SSL *ssl)
{
/* test heartbeat received (write_p is set to 0
for a received record) */
if ((content_type == TLS1_RT_HEARTBEAT) && (write_p == 0)) {
struct ssl_sock_ctx *ctx = __conn_get_ssl_sock_ctx(conn);
struct connection *conn = ssl_sock_get_conn(ssl, NULL);
struct ssl_sock_ctx *ctx = NULL;
const unsigned char *p = buf;
unsigned int payload;
ctx->xprt_st |= SSL_SOCK_RECV_HEARTBEAT;
/* <conn> may be NULL in QUIC context */
if (conn) {
ctx = __conn_get_ssl_sock_ctx(conn);
ctx->xprt_st |= SSL_SOCK_RECV_HEARTBEAT;
}
/* Check if this is a CVE-2014-0160 exploitation attempt. */
if (*p != TLS1_HB_REQUEST)
@ -1838,7 +1848,7 @@ static void ssl_sock_parse_heartbeat(struct connection *conn, int write_p, int v
}
#endif
static void ssl_sock_parse_clienthello(struct connection *conn, int write_p, int version,
static void ssl_sock_parse_clienthello(int write_p, int version,
int content_type, const void *buf, size_t len,
SSL *ssl)
{
@ -2132,7 +2142,7 @@ static void ssl_sock_parse_clienthello(struct connection *conn, int write_p, int
#ifdef HAVE_SSL_KEYLOG
static void ssl_init_keylog(struct connection *conn, int write_p, int version,
static void ssl_init_keylog(int write_p, int version,
int content_type, const void *buf, size_t len,
SSL *ssl)
{
@ -2155,20 +2165,13 @@ static void ssl_init_keylog(struct connection *conn, int write_p, int version,
/* Callback is called for ssl protocol analyse */
static __maybe_unused void ssl_sock_msgcbk(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg)
{
struct connection *conn = ssl_sock_get_conn(ssl, NULL);
struct ssl_sock_msg_callback *cbk;
/* The connection be NULL only for QUIC which does not free its SSL object
* as this done for TCP.
*/
if (!conn)
return;
/* Try to call all callback functions that were registered by using
* ssl_sock_register_msg_callback().
*/
list_for_each_entry(cbk, &ssl_sock_msg_callbacks, list) {
cbk->func(conn, write_p, version, content_type, buf, len, ssl);
cbk->func(write_p, version, content_type, buf, len, ssl);
}
}
@ -3813,6 +3816,8 @@ int ssl_sock_passwd_cb(char *buf, int size, int rwflag, void *userdata)
if (!data || data->passphrase_idx == -1)
return -1;
data->callback_called = 1;
ckch_data = data->ckch_data;
if (ckch_data)
@ -3832,13 +3837,16 @@ int ssl_sock_passwd_cb(char *buf, int size, int rwflag, void *userdata)
global_ssl.passphrase_cmd[1] = strdup(data->path);
if (!global_ssl.passphrase_cmd[1]) {
data->passphrase_idx = -1;
ha_alert("ssl_sock_passwd_cb: allocation failure\n");
return -1;
}
if (!passphrase_cache)
if (ssl_sock_create_passphrase_cache())
if (ssl_sock_create_passphrase_cache()) {
data->passphrase_idx = -1;
return -1;
}
/* Try all the already known passphrases first. */
if (data->passphrase_idx < passphrase_idx) {
@ -4074,6 +4082,11 @@ ssl_sock_initial_ctx(struct bind_conf *bind_conf)
options |= SSL_OP_NO_RENEGOTIATION;
#endif
#ifdef SSL_OP_NO_RX_CERTIFICATE_COMPRESSION
if (global_ssl.certificate_compression == 0)
options |= SSL_OP_NO_RX_CERTIFICATE_COMPRESSION | SSL_OP_NO_TX_CERTIFICATE_COMPRESSION;
#endif
SSL_CTX_set_options(ctx, options);
#ifdef SSL_MODE_ASYNC
@ -5127,6 +5140,11 @@ static int ssl_sock_prepare_srv_ssl_ctx(const struct server *srv, SSL_CTX *ctx)
options &= ~SSL_OP_NO_RENEGOTIATION;
#endif
#ifdef SSL_OP_NO_RX_CERTIFICATE_COMPRESSION
if (global_ssl.certificate_compression == 0)
options |= SSL_OP_NO_RX_CERTIFICATE_COMPRESSION | SSL_OP_NO_TX_CERTIFICATE_COMPRESSION;
#endif
SSL_CTX_set_options(ctx, options);
#ifdef SSL_MODE_ASYNC
@ -8536,6 +8554,7 @@ static void ssl_register_build_options()
#if defined(USE_OPENSSL) && (HA_OPENSSL_VERSION_NUMBER < 0x3000000fL)
memprintf(&ptr, "%s\nSSL library FIPS mode : %s", ptr, FIPS_mode() ? "yes" : "no");
#endif
memprintf(&ptr, "%s\nSSL library default verify directory : %s", ptr, ha_default_cert_dir());
memprintf(&ptr, "%s\nSSL library supports :", ptr);
for (i = CONF_TLSV_MIN; i <= CONF_TLSV_MAX; i++)
if (methodVersions[i].option)

View file

@ -5968,7 +5968,9 @@ static void stkt_late_init(void)
f = find_sample_fetch("src", strlen("src"));
if (f)
smp_fetch_src = f->process;
stkt_create_stk_ctr_pool();
if (stkt_create_stk_ctr_pool() & (ERR_ABORT | ERR_FATAL))
exit(1); // error already reported by the function
for (i = 0; i < CONFIG_HAP_TBL_BUCKETS; i++) {
MT_LIST_INIT(&per_bucket[i].toadd_tables);

View file

@ -970,6 +970,12 @@ static int tcp_parse_request_rule(char **args, int arg, int section_type,
hdr->namelen = 0;
hdr->len = len;
hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
if (!hdr->pool) {
memprintf(err, "parsing [%s:%d] : out of memory", file, line);
free(hdr);
release_sample_expr(expr);
return -1;
}
hdr->index = curpx->nb_req_cap++;
curpx->req_cap = hdr;

View file

@ -4943,6 +4943,35 @@ int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, c
"01" /* COM_QUIT command */
};
/* MySQL >=8.0 client Authentication packet with CLIENT_PLUGIN_AUTH capability.
* MySQL 8.0 changed the default authentication plugin from mysql_native_password
* to caching_sha2_password. By setting CLIENT_PLUGIN_AUTH and specifying
* mysql_native_password as the auth plugin, we can still perform health checks
* against MySQL 8.x servers when the health check user is configured with
* mysql_native_password authentication.
*
* Client capabilities: 0x00088200 (little-endian: 00820800)
* - CLIENT_PROTOCOL_41 (0x00000200)
* - CLIENT_SECURE_CONNECTION (0x00008000)
* - CLIENT_PLUGIN_AUTH (0x00080000)
*/
static char mysql80_rsname[] = "*mysql80-check";
static char mysql80_req[] = {
"%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
"00820800" /* client capabilities with CLIENT_PLUGIN_AUTH */
"00800001" /* max packet */
"21" /* character set (UTF-8) */
"000000000000000000000000" /* 23 bytes, all zeroes */
"0000000000000000000000"
"%[var(check.username),hex]00" /* the username */
"00" /* auth response length (0 = no password) */
"6d7973716c5f6e61746976655f" /* auth plugin name: "mysql_native_password\0" */
"70617373776f726400"
"010000" /* packet length */
"00" /* sequence ID */
"01" /* COM_QUIT command */
};
struct tcpcheck_ruleset *rs = NULL;
struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
struct tcpcheck_rule *chk;
@ -4999,8 +5028,14 @@ int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, c
mysql_req = mysql40_req;
mysql_rsname = mysql40_rsname;
}
else if (strcmp(args[cur_arg+2], "post-80") == 0) {
/* post-80: CLIENT_PLUGIN_AUTH + mysql_native_password (22 bytes) */
packetlen = userlen + 7 + 27 + 22;
mysql_req = mysql80_req;
mysql_rsname = mysql80_rsname;
}
else {
ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41', 'pre-41' and 'post-80' (got '%s').\n",
file, line, args[cur_arg], args[cur_arg+2]);
goto error;
}

View file

@ -1394,6 +1394,12 @@ int thread_map_to_groups()
for (g = 0; g < global.nbtgroups; g++) {
if (!ha_tgroup_info[g].count)
ug++;
else {
if (ha_tgroup_info[g].count > global.maxthrpertgroup) {
ha_alert("thread-group %d assigned too many threads (%d, max=%d)\n", g, ha_tgroup_info[g].count, global.maxthrpertgroup);
return -1;
}
}
ha_tgroup_info[g].tgid_bit = 1UL << g;
}
@ -1641,9 +1647,6 @@ void thread_detect_count(void)
if (global.nbtgroups)
grp_min = grp_max = global.nbtgroups;
if (!global.maxthrpertgroup)
global.maxthrpertgroup = MAX_THREADS_PER_GROUP;
#if defined(USE_THREAD)
/* Adjust to boot settings if not forced */
if (thr_min <= thread_cpus_enabled_at_boot && thread_cpus_enabled_at_boot < thr_max)
@ -2138,11 +2141,6 @@ static int cfg_parse_thread_group(char **args, int section_type, struct proxy *c
return -1;
}
if (ha_tgroup_info[tgroup-1].count > global.maxthrpertgroup) {
memprintf(err, "'%s %ld' assigned too many threads (%d, max=%d)", args[0], tgroup, tot, global.maxthrpertgroup);
return -1;
}
return 0;
}

View file

@ -987,6 +987,11 @@ struct sockaddr_storage *str2sa_range(const char *str, int *port, int *low, int
int new_fd = -1;
enum proto_type proto_type = 0; // to shut gcc warning
int ctrl_type = 0; // to shut gcc warning
/*
* Indicates that we want to use an alternate protocol instead of the
* default one.
* Currently, only MPTCP is defined as an alternate protocol for TCP
*/
int alt_proto = 0;
portl = porth = porta = 0;
@ -1011,7 +1016,6 @@ struct sockaddr_storage *str2sa_range(const char *str, int *port, int *low, int
((opts & (PA_O_STREAM|PA_O_DGRAM)) == (PA_O_DGRAM|PA_O_STREAM) && (opts & PA_O_DEFAULT_DGRAM))) {
proto_type = PROTO_TYPE_DGRAM;
ctrl_type = SOCK_DGRAM;
alt_proto = 1;
} else {
proto_type = PROTO_TYPE_STREAM;
ctrl_type = SOCK_STREAM;
@ -1026,7 +1030,6 @@ struct sockaddr_storage *str2sa_range(const char *str, int *port, int *low, int
str2 += 6;
proto_type = PROTO_TYPE_DGRAM;
ctrl_type = SOCK_DGRAM;
alt_proto = 1;
}
else if (strncmp(str2, "quic+", 5) == 0) {
str2 += 5;
@ -1043,7 +1046,6 @@ struct sockaddr_storage *str2sa_range(const char *str, int *port, int *low, int
ss.ss_family = AF_UNIX;
proto_type = PROTO_TYPE_DGRAM;
ctrl_type = SOCK_DGRAM;
alt_proto = 1;
}
else if (strncmp(str2, "uxst@", 5) == 0) {
str2 += 5;
@ -1089,7 +1091,6 @@ struct sockaddr_storage *str2sa_range(const char *str, int *port, int *low, int
ss.ss_family = AF_INET;
proto_type = PROTO_TYPE_DGRAM;
ctrl_type = SOCK_DGRAM;
alt_proto = 1;
}
else if (strncmp(str2, "tcp6@", 5) == 0) {
str2 += 5;
@ -1109,7 +1110,6 @@ struct sockaddr_storage *str2sa_range(const char *str, int *port, int *low, int
ss.ss_family = AF_INET6;
proto_type = PROTO_TYPE_DGRAM;
ctrl_type = SOCK_DGRAM;
alt_proto = 1;
}
else if (strncmp(str2, "tcp@", 4) == 0) {
str2 += 4;
@ -1129,7 +1129,6 @@ struct sockaddr_storage *str2sa_range(const char *str, int *port, int *low, int
ss.ss_family = AF_UNSPEC;
proto_type = PROTO_TYPE_DGRAM;
ctrl_type = SOCK_DGRAM;
alt_proto = 1;
}
else if (strncmp(str2, "quic4@", 6) == 0) {
str2 += 6;
@ -1399,6 +1398,8 @@ struct sockaddr_storage *str2sa_range(const char *str, int *port, int *low, int
}
if (proto || (opts & PA_O_CONNECT)) {
// if the socket type is SOCK_DGRAM, use by default an alternate protocol
alt_proto = alt_proto || (ctrl_type == SOCK_DGRAM);
/* Note: if the caller asks for a proto, we must find one,
* except if we inherit from a raw FD (family == AF_CUST_EXISTING_FD)
* orif we return with an fqdn that will resolve later,