]> git.kaiwu.me - haproxy.git/log
haproxy.git
4 weeks agoCLEANUP: cfgparse: remove the unused common_kw_list[] definitions 20261006-review-cfgparse-2
Willy Tarreau [Thu, 8 Oct 2026 17:03:58 +0000 (19:03 +0200)]
CLEANUP: cfgparse: remove the unused common_kw_list[] definitions

These were the old (and incomplete) lists of section names for use
with cfg_parse_global() and cfg_parse_listen(), that were passed to
cfg_find_best_match(). They're no longer needed so let's get rid of
them.

4 weeks agoMINOR: cfgparse: always suggest the section names on an unknown keyword
Willy Tarreau [Thu, 8 Oct 2026 17:31:42 +0000 (19:31 +0200)]
MINOR: cfgparse: always suggest the section names on an unknown keyword

When an unknown keyword is met in a section, cfg_find_best_match() looks
for the closest match among the keywords registered for that section,
plus an optional list of extra words passed by the caller, which
cfg_parse_global() and cfg_parse_listen() were using to pass a list of
a few known hard-coded section names. But this resulted several times
in some names not being matched (e.g. "resolvers" which was later added
but only for "global"), or others still not being known, e.g.

  $ printf 'global\n\tcrt-stor\n' | ./haproxy -c -f /dev/stdin
  [ALERT] : parsing [/dev/stdin:2] : unknown keyword 'crt-stor' in
    'global' section; did you mean 'crt-store' maybe ?

Let's have the function always consider all known section names since
they are always permitted, that is much more effective and will no
longer require to hard-code certain combinations. This completely drops
the extra argument that's no longer used.

4 weeks agoCLEANUP: cfgparse-listen: simplify the second half of cfg_parse_listen()
Willy Tarreau [Thu, 8 Oct 2026 16:48:46 +0000 (18:48 +0200)]
CLEANUP: cfgparse-listen: simplify the second half of cfg_parse_listen()

All the proxy keywords are now registered in cfg_kws, so the function
only has to create the sections ("listen", "frontend", "backend" and
"defaults") and to look the keyword up in the registered lists. The
historical indented block that was dealing with known keywords is no
longer necessary and it's the right moment to reindent the few tens
of remaining lines.

4 weeks agoCLEANUP: cfgparse-listen: move "option" to its own parser
Willy Tarreau [Thu, 8 Oct 2026 15:58:18 +0000 (17:58 +0200)]
CLEANUP: cfgparse-listen: move "option" to its own parser

This was by far the largest block of cfg_parse_listen(), about 470
lines. It is now registered in cfg_kws and parsed by
proxy_parse_option(), making the keyword discoverable via "-dKcfg". The
keyword modifier is taken from cfg_curr_kwm since the keyword parsers
don't receive it.

The whole block of non-standard options was a bit rearranged by
dependencies or behaviors so as to factor some code, mode checks and
flags setting.

4 weeks agoMINOR: log: add proxy_logformat_origin() to detect a proxy's log-format type
Willy Tarreau [Thu, 8 Oct 2026 16:42:16 +0000 (18:42 +0200)]
MINOR: log: add proxy_logformat_origin() to detect a proxy's log-format type

This new function will check the proxy's logformat.str and return return
an option name (e.g. "option httplog") or a directive ("log-format") that
will permit error messages to report where a log-format had been set.

4 weeks agoCLEANUP: cfgparse-listen: move "bind" to its own parser
Willy Tarreau [Thu, 8 Oct 2026 15:53:44 +0000 (17:53 +0200)]
CLEANUP: cfgparse-listen: move "bind" to its own parser

The keyword is now registered in cfg_kws and parsed by
proxy_parse_bind(), making it discoverable via "-dKcfg".

The now unused bind_conf variable and alloc_error label were removed
from cfg_parse_listen() to avoid warnings.

4 weeks agoCLENAUP: cfgparse-listen: move the "server" keywords to their own parser
Willy Tarreau [Thu, 8 Oct 2026 15:49:55 +0000 (17:49 +0200)]
CLENAUP: cfgparse-listen: move the "server" keywords to their own parser

"server", "default-server" and "server-template" only differ by the
flags they pass to parse_server(), so they were moved together to a new
proxy_parse_server() function registered in cfg_kws, making them
discoverable via "-dKcfg". parse_server() emits its own messages, so the
new parser only has to turn a fatal error into a negative return value.

4 weeks agoCLEANUP: cfgparse-listen: move "stats" to its own parser
Willy Tarreau [Thu, 8 Oct 2026 15:31:05 +0000 (17:31 +0200)]
CLEANUP: cfgparse-listen: move "stats" to its own parser

The keyword is now registered in cfg_kws and parsed by
proxy_parse_stats(), making it discoverable via "-dKcfg". It needs the
default proxy and takes it via the <defpx> argument.

Like for previous http-request rules, the "http-request stats" action
will now return only the first failure and not all of them at once.

Note that <acl_cond> was removed from cfg_parse_listen() to avoid a
build warning since it was the last part using it, we're getting
closer to the end!

4 weeks agoCLENAUP: cfgparse-listen: move "email-alert" to its own parser
Willy Tarreau [Thu, 8 Oct 2026 15:21:29 +0000 (17:21 +0200)]
CLENAUP: cfgparse-listen: move "email-alert" to its own parser

The keyword is now registered in cfg_kws and parsed by
proxy_parse_email_alert(), making it discoverable via "-dKcfg".

The "from", "mailers", "myhostname" and "to" options only differ by the
field they assign, so the field is now selected first and the rest of
the processing, including the check for a missing value, is shared.
~25% code removed in the operation.

4 weeks agoCLEANUP: cfgparse-listen: move "cookie" to its own parser
Willy Tarreau [Thu, 8 Oct 2026 14:00:17 +0000 (16:00 +0200)]
CLEANUP: cfgparse-listen: move "cookie" to its own parser

The keyword is now registered in cfg_kws and parsed by
proxy_parse_cookie(), making it discoverable via "-dKcfg".

The "maxidle" and "maxlife" options were essentially re-merged since
they differed by only the struct member receiving the parsed value.
The code was refactored a little bit since it never happened over the
years when keywords were added, but overall nothing fancy.

There's one minor change though, that's imposed by keyword parsers:
previously it was possible to report up to 3 alerts if multiple args
were invalid, but now it's no longer possible since keyword parsers
can only report one error, so only the first one is reported. No big
deal.

4 weeks agoCLEANUP: cfgparse-listen: move "stick-table" and "stick" to their own parser
Willy Tarreau [Thu, 8 Oct 2026 13:53:59 +0000 (15:53 +0200)]
CLEANUP: cfgparse-listen: move "stick-table" and "stick" to their own parser

Both keywords configure the stick-table (or rules) and are forbidden in
a defaults section. They were moved together to a new proxy_parse_stick()
function registered in cfg_kws, making them discoverable via "-dKcfg".

The code is mostly the same, with error paths reworked to avoid chains of
free()+return calls in error paths.

4 weeks agoCLEANUP: cfgparse-listen: move "redirect" to its own parser
Willy Tarreau [Thu, 8 Oct 2026 13:51:21 +0000 (15:51 +0200)]
CLEANUP: cfgparse-listen: move "redirect" to its own parser

The keyword is now registered in cfg_kws and parsed by
proxy_parse_redirect(), making it discoverable via "-dKcfg". It
couldn't be merged with the http-request rules because the rules
are of type redirect_rule while the other ones were act_rule.

4 weeks agoCLEANUP: cfgparse-listen: move the HTTP rule sets to their own parser
Willy Tarreau [Thu, 8 Oct 2026 13:45:14 +0000 (15:45 +0200)]
CLEANUP: cfgparse-listen: move the HTTP rule sets to their own parser

"http-request", "http-response" and "http-after-response" were handled
by three same blocks which only differed by the rule list, the parsing
function and the validity masks. They were moved to a new function
proxy_parse_http_rules(), making the keywords discoverable via "-dKcfg".

Note that a failure to parse the action now returns a fatal error
instead of immediately aborting the config parsing, so that some of
the remaining lines are still checked.

4 weeks agoCLEANUP: cfgparse-listen: add parser for server/backend sekection keywords
Willy Tarreau [Thu, 8 Oct 2026 09:33:00 +0000 (11:33 +0200)]
CLEANUP: cfgparse-listen: add parser for server/backend sekection keywords

Let's group "default_backend", "use_backend" and "use-server" into their
own parser proxy_parse_use_backend() registered in cfg_kws, making them
discoverable via "-dKcfg". Not much changes.

4 weeks agoCLEANUP: cfgparse-listen: move "source" "usesrc" and "interface" to their own parser
Willy Tarreau [Thu, 8 Oct 2026 09:20:49 +0000 (11:20 +0200)]
CLEANUP: cfgparse-listen: move "source" "usesrc" and "interface" to their own parser

The keywords are now registered in cfg_kws and parsed by
proxy_parse_source(), making them discoverable via "-dKcfg". The loop
was slightly reorganized. We cannot really use the BUG_ON() trap here
for unregistered keywords due to the loop that tries to consume as many
sub-keywords as possible (since "usesrc" and "interface" are in fact
options of "source" parsed as if they were first-level ones).

4 weeks agoCLEANUP: cfgparse-listen: move the load-balancing keywords to their own parser
Willy Tarreau [Thu, 8 Oct 2026 09:19:08 +0000 (11:19 +0200)]
CLEANUP: cfgparse-listen: move the load-balancing keywords to their own parser

"balance", "hash-type" and "hash-balance-factor" are used to configure
the load balancing and require the backend capability, so they were moved
together to a new proxy_parse_balance() function registered in cfg_kws,
making them discoverable via "-dKcfg". As usual, backend_parse_balance()
now needs to take care of releasing the error message.

4 weeks agoCLEANUP: cfgparse-listen: add parser for "log", "log-tag" and "unique-id-header"
Willy Tarreau [Thu, 8 Oct 2026 09:14:48 +0000 (11:14 +0200)]
CLEANUP: cfgparse-listen: add parser for "log", "log-tag" and "unique-id-header"

The three keywords were moved to a new proxy_parse_log_opts() function
registered in cfg_kws, making them discoverable via "-dKcfg". The "log"
keyword consults cfg_curr_kwm to detect "no log", and the header name is
now allocated before the previous one is released. Arguably we could have
moved unique-id-header in its own function, or move them all to another
existing function, this doesn't change much. They were taken together
as they were grouped in the original code block.

4 weeks agoCLEANUP: cfgparse-listen: move the log format keywords to their own parser
Willy Tarreau [Wed, 7 Oct 2026 16:52:10 +0000 (18:52 +0200)]
CLEANUP: cfgparse-listen: move the log format keywords to their own parser

"log-format", "log-format-sd", "error-log-format" and "unique-id-format"
all assign a log format expression to the proxy and were handled almost
the same way, so they were moved together to proxy_parse_logformat() and
listed in cfg_kws, making them discoverable via "-dKcfg".

All common parts (arg check, allocation, warning about backends being
ignored) are now shared and performed first. The result of strdup() of
the file name is now checked. The code is cleaner and smaller:

   90 insertions(+), 124 deletions(-)

4 weeks agoCLEANUP: cfgparse-listen: move "capture" to its own parser
Willy Tarreau [Wed, 7 Oct 2026 16:46:49 +0000 (18:46 +0200)]
CLEANUP: cfgparse-listen: move "capture" to its own parser

The keyword is now registered in cfg_kws and parsed by
proxy_parse_capture(), making it discoverable via "-dKcfg".

Note that the code was rearranged a bit so that the checks for defaults
section that were common between the 3 variants are now made only once
at the beginning of the function. A little bit more factoring was done
but overall nothing fancy.

4 weeks agoCLEANUP: cfgparse-listen: move the persistence keywords to their own parser
Willy Tarreau [Wed, 7 Oct 2026 16:28:27 +0000 (18:28 +0200)]
CLEANUP: cfgparse-listen: move the persistence keywords to their own parser

"persist", "force-persist" and "ignore-persist" were moved to a new
proxy_parse_persist() function registered in cfg_kws, making them
discoverable via "-dKcfg". Just a few cosmetic changes again.

4 weeks agoCLEANUP: cfgparse-listen: move the monitor keywords to their own parser
Willy Tarreau [Wed, 7 Oct 2026 16:25:52 +0000 (18:25 +0200)]
CLEANUP: cfgparse-listen: move the monitor keywords to their own parser

"monitor-uri" and "monitor" both configure the monitoring requests and
require the frontend capability, so they were moved together to a new
proxy_parse_monitor() function registered in cfg_kws, making them
discoverable via "-dKcfg".

The "monitor" keyword needs to release the error message reported by
build_acl_cond().

4 weeks agoCLEANUP: cfgparse-listen: move "acl" to its own parser
Willy Tarreau [Wed, 7 Oct 2026 16:16:50 +0000 (18:16 +0200)]
CLEANUP: cfgparse-listen: move "acl" to its own parser

The keyword is now registered in cfg_kws and parsed by proxy_parse_acl(),
making it discoverable via "-dKcfg". Care was taken to release the
errptr returned by parse_acl().

The code could also have been moved to acl.c but it appeared strange there
because acl.c doesn't really deal with first-level keywords and doesn't
care about proxies, and here we're in a proxy section, so aftre all,
cfgparse-listen seems to remain the best place for the function.

4 weeks agoCLEANUP: cfgparse-listen: move "id" and "description" to their own parser
Willy Tarreau [Wed, 7 Oct 2026 16:12:02 +0000 (18:12 +0200)]
CLEANUP: cfgparse-listen: move "id" and "description" to their own parser

Both keywords set the name and desc of the proxy and are both
forbidden in a defaults section, so they were moved together to a new
proxy_parse_id_desc() function registered in cfg_kws, making them
discoverable via "-dKcfg". Nothing really special here.

4 weeks agoCLEANUP: cfgparse-listen: move "mode" to its own parser
Willy Tarreau [Wed, 7 Oct 2026 16:07:26 +0000 (18:07 +0200)]
CLEANUP: cfgparse-listen: move "mode" to its own parser

The keyword is now registered in cfg_kws and parsed by proxy_parse_mode(),
making it discoverable via "-dKcfg". The core remains essentially the
same.

4 weeks agoCLEANUP: cfgparse-listen: move the backend one-arg keywords to their own parser
Willy Tarreau [Wed, 7 Oct 2026 16:05:59 +0000 (18:05 +0200)]
CLEANUP: cfgparse-listen: move the backend one-arg keywords to their own parser

"retries", "http-reuse", "http-send-name-header", "dynamic-cookie-key",
"load-server-state-from-file" and "server-state-file-name" all require
the backend capability and take a single argument, so they were moved
together to a new proxy_parse_be_opts() function registered in cfg_kws,
making them discoverable via "-dKcfg". The code was slightly cleaned up
while moved, but nothing sensitive (removed some flag dance which already
caused bugs in the past, etc).

4 weeks agoCLEANUP: cfgparse-listen: move the connection limits to thier own parser
Willy Tarreau [Wed, 7 Oct 2026 16:00:44 +0000 (18:00 +0200)]
CLEANUP: cfgparse-listen: move the connection limits to thier own parser

"maxconn", "backlog", "fullconn" and "max-session-srv-conns" all take a
single integer argument and only differ by the capability they require
and the field they assign, so they were moved together to a new
proxy_parse_conn_limits() function registered in cfg_kws, making them
discoverable via "-dKcfg".

4 weeks agoCLEANUP: cfgparse-listen: move "disabled" and "enabled" to their own parser
Willy Tarreau [Wed, 7 Oct 2026 15:59:26 +0000 (17:59 +0200)]
CLEANUP: cfgparse-listen: move "disabled" and "enabled" to their own parser

Both keywords only set or clear PR_FL_DISABLED, so they were moved
together to a new proxy_parse_enabled() function registered in cfg_kws,
making them discoverable via "-dKcfg".

4 weeks agoMEDIUM: cfgparse-listen: move removed keywords' help messages into an array
Willy Tarreau [Wed, 7 Oct 2026 15:50:23 +0000 (17:50 +0200)]
MEDIUM: cfgparse-listen: move removed keywords' help messages into an array

The 29 remaining removed keywords are almost all handled the same way:
  - the version where the keyword was removed is reported (except for
    "bind-process")
  - a help message is almost always provided

Instead of writing lots of code that is not always consistent, better
place these keywords in an array with their version and help messages,
leaving NULL for the missing info. For "bind-process", the relevant
version was updated (2.7).

The proxy_parse_removed_kw() parser was extended to consult this table,
and the keywords only had to be added to the standard parser, pointing
to this function as their parser. This way, the same output messages as
before are emitted with much less code and even less risks of errors.

The list of converted keywords is:

  appsession, bind-process, block, cliexp, grace, redisp, redispatch,
  reqadd, reqallow, reqdel, reqdeny, reqiallow, reqidel, reqideny,
  reqipass, reqirep, reqitarpit, reqpass, reqrep, reqtarpit, rspadd,
  rspdel, rspdeny, rspidel, rspideny, rspirep, rsprep, srvexp,
  transparent,

4 weeks agoMINOR: cfgparse-listen: introduce proxy_parse_removed_kw() and use it
Willy Tarreau [Wed, 7 Oct 2026 15:46:51 +0000 (17:46 +0200)]
MINOR: cfgparse-listen: introduce proxy_parse_removed_kw() and use it

A significant number of cfgparse_listen()'s keywords are handled
specifically because they're obsolete and continue to deliver a detailed
indication about what to use instead. The most complicated ones are
"dispatch" and "monitor-net" which even suggest the exact line to use
in order to replace them.

Let's introduce proxy_parse_removed_kw() to handle such keywords and
their excentricities. The function is just a regular keyword parser,
so that the keywords it handles are now in the standard array and point
to it.

For now we only implement "monitor-net" and "dispatch". The output
messages remain exactly the same.

4 weeks agoCLEANUP: cfgparse-global: simplify cfg_parse_global()
Willy Tarreau [Wed, 7 Oct 2026 15:34:57 +0000 (17:34 +0200)]
CLEANUP: cfgparse-global: simplify cfg_parse_global()

Now that all the keywords are registered in cfg_kws, the function only
has to deal with the "global" section name itself and to lookup the
keyword in the registered lists. The "discovery_kw" label and the old
"else" block are no longer used and were removed. The function which
used to be ~1000 lines in master is now only ~60.

4 weeks agoCLEANUP: cfgparse-global: ask not to add new keywords in common_kw_list[]
Willy Tarreau [Wed, 7 Oct 2026 15:29:21 +0000 (17:29 +0200)]
CLEANUP: cfgparse-global: ask not to add new keywords in common_kw_list[]

Better discourage new keywords from being added again to the remaining
list, which currently still contains a few section names that are not
discovered otherwise. We just add a comment.

4 weeks agoCLEANUP: cfgparse-global: move "anonkey" to its own parser
Willy Tarreau [Wed, 7 Oct 2026 15:25:15 +0000 (17:25 +0200)]
CLEANUP: cfgparse-global: move "anonkey" to its own parser

The keyword is now registered in cfg_kws and parsed by
cfg_parse_global_anonkey(), making it discoverable via "-dKcfg".

This was the last keyword parsed by strcmp() in cfg_parse_global().

4 weeks agoCLEANUP: cfgparse-global: move "cpu-map" to its own parser
Willy Tarreau [Wed, 7 Oct 2026 15:18:43 +0000 (17:18 +0200)]
CLEANUP: cfgparse-global: move "cpu-map" to its own parser

The keyword is now registered in cfg_kws and parsed by
cfg_parse_global_cpu_map(), making it discoverable via "-dKcfg". The
code is mostly unchanged, except that the errmsg returned but the
called function now has to be freed here (it was done in the
cfgparse_global loop before)

4 weeks agoCLEANUP: cfgparse-global: move the spread-checks keywords to their own parser
Willy Tarreau [Wed, 7 Oct 2026 15:13:35 +0000 (17:13 +0200)]
CLEANUP: cfgparse-global: move the spread-checks keywords to their own parser

"spread-checks" and "max-spread-checks" both control how the health
checks are spread over time, so they were moved together to a new
cfg_parse_global_spread_checks() function and registered in cfg_kws so
as to become discoverable via "-dKcfg".

The "already specified" message now uses a warning.

4 weeks agoCLEANUP: cfgparse-global: move the state file keywords to their own parser
Willy Tarreau [Wed, 7 Oct 2026 15:10:19 +0000 (17:10 +0200)]
CLEANUP: cfgparse-global: move the state file keywords to their own parser

"server-state-base", "server-state-file" and "stats-file" all store a
path which is used to preload some state at boot, and were handled
the same way, so let's move them to cfg_parse_global_state_files()
so as to become discoverable via "-dKcfg".

Like for a few previous commits, the result of the strdup() is now
checked, and the "already specified" warning issues a warning.

4 weeks agoCLEANUP: cfgparse-global: move the log keywords to a single parser
Willy Tarreau [Wed, 7 Oct 2026 14:48:59 +0000 (16:48 +0200)]
CLEANUP: cfgparse-global: move the log keywords to a single parser

"log", "log-send-hostname" and "log-tag" are all related to the logging
setup, so they were moved together to a new cfg_parse_global_log_opts()
function and registered in cfg_kws so as to become discoverable via
"-dKcfg". The "log" keyword uses cfg_curr_kwm to detect "no log".

Like for previous commits, the result of the strdup() of the hostname
is now checked, and the duplicate hostname only emits a warning.

4 weeks agoCLEANUP: cfgparse-global: move "unix-bind" to its own parser
Willy Tarreau [Wed, 7 Oct 2026 14:44:49 +0000 (16:44 +0200)]
CLEANUP: cfgparse-global: move "unix-bind" to its own parser

The "unix-bind" keyword even had its own args loop in cfg_parse_global().
It's now registered in cfg_kws and parsed by cfg_parse_global_unix_bind(),
making it discoverable via "-dKcfg". The code was slightly restructured,
particularly the long chain of "if (...) { ...; continue; }" was simplified
a bit.

Like in a few previous patches, the duplicate test for "prefix" now just
emits a warning, and the result of strdup() is tested.

4 weeks agoCLEANUP: cfgparse-global: move "node" and "description" to their own parser
Willy Tarreau [Wed, 7 Oct 2026 14:41:09 +0000 (16:41 +0200)]
CLEANUP: cfgparse-global: move "node" and "description" to their own parser

Both keywords give an name/desc to the current node, so they were moved
together to a new cfg_parse_global_node_desc() function and registered in
cfg_kws so as to become discoverable via "-dKcfg".

While doing this, a test on strdup(node) result was added so as to fail
cleanly on memory allocation failure (this remains i nthe scope of a
cleanup since in practice it never happens).

4 weeks agoCLEANUP: cfgparse-global: move "ssl-server-verify" to its own parser
Willy Tarreau [Wed, 7 Oct 2026 14:35:56 +0000 (16:35 +0200)]
CLEANUP: cfgparse-global: move "ssl-server-verify" to its own parser

The keyword is now registered in cfg_kws and parsed by
cfg_parse_global_ssl_server_verify(), making it discoverable via
"-dKcfg".

4 weeks agoCLEANUP: cfgparse-global: move the compression limits to a single parser
Willy Tarreau [Wed, 7 Oct 2026 14:35:35 +0000 (16:35 +0200)]
CLEANUP: cfgparse-global: move the compression limits to a single parser

"maxcomprate", "maxzlibmem" and "maxcompcpuusage" all limit the resources
which may be dedicated to the compression, so they were moved together to
a new cfg_parse_global_comp_limits() function and registered in cfg_kws
so as to become discoverable via "-dKcfg".

4 weeks agoCLEANUP: cfgparse-global: move the process-wide limits to a single parser
Willy Tarreau [Wed, 7 Oct 2026 14:35:28 +0000 (16:35 +0200)]
CLEANUP: cfgparse-global: move the process-wide limits to a single parser

"maxconn", "maxconnrate", "maxpipes", "maxsessrate", "maxsslrate",
"fd-hard-limit" and "ulimit-n" all take a single integer argument, may
only be specified once and simply assign a field in the global struct,
so they were moved together to a new cfg_parse_global_limits() function
and registered in cfg_kws so as to become discoverable via "-dKcfg".

The arguments are now checked before the value is assigned rather than
after. The two non-fatal alerts ("already specified" and "maxconn too high")
are emitted directly via ha_warning() / ha_alert(). (Same as for previous
patch, "already specified" turned to a warning) since more appropriate.

4 weeks agoCLEANUP: cfgparse-global: move "cluster-secret" to its own parser
Willy Tarreau [Wed, 7 Oct 2026 14:28:20 +0000 (16:28 +0200)]
CLEANUP: cfgparse-global: move "cluster-secret" to its own parser

The keyword is now registered in cfg_kws and parsed by
cfg_parse_global_cluster_secret(), making it discoverable via "-dKcfg".
Note that using ha_alert() to warn about a repetition did not make much
sense since it didn't declare a fatal error, and since it doesn't prevent
from starting, it's better turned to a warning so that's what was done
here.

4 weeks agoCLEANUP: cfgparse-global: move "external-check" to its own parser
Willy Tarreau [Wed, 7 Oct 2026 14:24:32 +0000 (16:24 +0200)]
CLEANUP: cfgparse-global: move "external-check" to its own parser

The keyword is now registered in cfg_kws and parsed by
cfg_parse_global_external_check(), making it discoverable via "-dKcfg".

4 weeks agoCLEANUP: cfgparse-global: move "uid", "gid", "user" and "group" to its own parser
Willy Tarreau [Wed, 7 Oct 2026 14:22:30 +0000 (16:22 +0200)]
CLEANUP: cfgparse-global: move "uid", "gid", "user" and "group" to its own parser

These 4 keywords all end up setting global.{uid,gid}, either from a
number or by resolving a system user or group name, so they were moved
together to a new cfg_parse_global_uid_gid() function and registered in
cfg_kws so as to become discoverable via "-dKcfg". The parser continues
to use ha_alert() / ha_warning() since keyword parsers can normally only
return a single message (e.g. "statically built libc" could be emitted
before the rest).

4 weeks agoCLEANUP: cfgparse-global: move "set-dumpable" to its own parser
Willy Tarreau [Wed, 7 Oct 2026 14:18:55 +0000 (16:18 +0200)]
CLEANUP: cfgparse-global: move "set-dumpable" to its own parser

The keyword is now registered in cfg_kws and parsed by
cfg_parse_global_set_dumpable(), so that it becomes discoverable via
"-dKcfg". It relies on cfg_curr_kwm to detect the "no" modifier.

4 weeks agoCLEANUP: cfgparse-global: move the argument-less boolean options to a parser
Willy Tarreau [Wed, 7 Oct 2026 14:15:59 +0000 (16:15 +0200)]
CLEANUP: cfgparse-global: move the argument-less boolean options to a parser

The following keywords were still parsed in cfg_parse_global() and were
not discoverable via "-dKcfg": "busy-polling", "nosplice", "nogetaddrinfo",
"noreuseport", "numa-cpu-mapping", "quick-exit", "strict-limits",
"limited-quic", "insecure-fork-wanted", "insecure-setuid-wanted" and
"h2-workaround-bogus-websocket-clients". They all take no argument and
just set or clear a boolean, so they were moved to a single new parser,
cfg_parse_global_bool_opts(), and registered in cfg_kws. The ones which
support the "no" modifier now consult cfg_curr_kwm for this.

The check for extra args is now factored and the "no" support is checked
at the upper level.

4 weeks agoMINOR: cfgparse: expose the keyword modifier to the keyword parsers
Willy Tarreau [Wed, 7 Oct 2026 14:06:44 +0000 (16:06 +0200)]
MINOR: cfgparse: expose the keyword modifier to the keyword parsers

Section parsers receive the keyword modifier ("no" or "default") found in
front of the keyword as their last argument, but the keyword parsers which
are registered in a cfg_kw_list only receive the arguments, the file name
and the line number. As a consequence, a keyword supporting negation such
as "busy-polling" cannot be moved out of a section parser.

Let's just expose the modifier of the line being parsed in a new global
variable, cfg_curr_kwm, which is set for each new line in readcfgfile()
and reset once the file is completely parsed. This way the few keyword
parsers which are interested in it may consult it, the same way they
already consult "file" and "line".

4 weeks agoCLEANUP: proxy: adapt the error message for removed "option transparent"
Willy Tarreau [Fri, 9 Oct 2026 07:26:46 +0000 (09:26 +0200)]
CLEANUP: proxy: adapt the error message for removed "option transparent"

The keyword was removed by commit be8159248f ("MAJOR: proxy: remove
support for "dispatch" and "transparent" proxy keywords"), but the
warning message for deprecated "option transparent" wasn't removed
now that the option may no longer be found.

4 weeks agoBUG/MINOR: cfgparse-listen: complete the list of options for the suggestions
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
BUG/MINOR: cfgparse-listen: complete the list of options for the suggestions

The options which need a dedicated parsing are not described in the
cfg_opts* arrays, so they are listed in common_options[] in order to be
proposed as a suggestion when one of them is mistyped. That list was
incomplete and was missing "accept-invalid-http-request",
"accept-invalid-http-response", "forceclose",
"http-restrict-req-hdr-names", "http-tunnel", "http_proxy", "httpslog"
and "use-small-buffers", and it spelled "originalto" as "original-to",
so none of these ever got suggested:

  $ printf 'backend b\noption originalt\n' | ./haproxy -c -f /dev/stdin
  [ALERT] : parsing [/dev/stdin:2] : unknown option 'originalt'.

In addition "spop-check" was listed unconditionally while the option
only exists when SPOE is enabled, so a build spoe would propose it
but fail to recognize it:

  [ALERT] : unknown option 'spop-chek'; did you mean 'spop-check' maybe ?
  ...
  [ALERT] : unknown option 'spop-check'; did you mean 'tcp-check' maybe ?

The list is now sorted, complete and conditional where needed, and it
received a comment saying that it must be kept in sync with the options.

Note that the existing cfg_opts* already handle a few special cases like
disabled or obsolete options. Maybe we could extend it to also support
special options that are just not parsed there and must appear somewhere
else. This could even further simplify the parser by preserving PR_CAP_*
to let the main parser handle incompatibilities.

This may be backported to all supported versions.

4 weeks agoBUG/MINOR: proxy: also look for the closest option among cfg_opts3
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
BUG/MINOR: proxy: also look for the closest option among cfg_opts3

proxy_find_best_option() scans cfg_opts[] and cfg_opts2[] for the option
which looks closest to a mistyped one. When cfg_opts3[] was introduced
in 3.2 with commit cc5a66212d ("MINOR: proxy: add proxy->options3"), it
was not added there, so such newly added options are not proposed as
candidates for mistakes (e.g. "assume-rfc6587-ntf" or "dont-parse-log").
Let's just fix this.

This may be backported to 3.2.

4 weeks agoBUG/MINOR: cfgparse-listen: properly reject extra args in a backend section
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
BUG/MINOR: cfgparse-listen: properly reject extra args in a backend section

A proxy section only accepts "<keyword> <name> [from <defaults>]", and
anything else was reported as an alert suggesting to use the "bind"
keyword, but only for sections with the frontend capability. For the
others the block was left silently, without any message and without
creating the proxy nor opening a bacend section! As such any keyword
so that either the next line was a section keyword and it would restart
from there, or any other proxy keyword was rejected with "'listen' or
'defaults' expected". The only really silent case was when the section
is the last one, as it's silently dropped:

  $ printf 'defaults\nmode http\nbackend b junk\n' | ./haproxy -c -f /dev/stdin
  $ echo 0
  0

This patch fixes the error report (both message and err_code) for
backends. It moves the too many args check after the fe/be distinction
so that we don't mix the two messages and we can always emit a relevant
message for both fronts and backs.

Since the only case where such a config would have been accepted was
when an empty and invalid backend definition was placed at the end
immediately after a defaults section, there's no risk to break any
working config so this can safely be backported.

4 weeks agoBUG/MINOR: cfgparse-listen: don't lose the errors reported while creating a proxy
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
BUG/MINOR: cfgparse-listen: don't lose the errors reported while creating a proxy

When a proxy inherits from its defaults section, the return value of
proxy_ref_defaults() was assigned to err_code instead of being ORed
into it, wiping all the errors collected earlier for that line. As a
result an invalid character in a proxy's name was reported but the
process started anyway:

  $ cat inv.cfg
  defaults
      mode http
  frontend inv@lid
      bind :8080
  $ ./haproxy -c -f inv.cfg
  [ALERT] : parsing [inv.cfg:3] : character '@' is not permitted in
    'frontend' name 'inv@lid'.
  $ echo 0
  0

The same applies to the duplicate name and ambiguous defaults reference
diagnostics emitted before the proxy is created.

Configurations which were mistakenly accepted until will now be rejected,
though they were already emitting an alert. The issue was only introduced
in 3.4 with commit 87ea407cce ("MINOR: proxy: refactor proxy inheritance
of a defaults section"), so it's fairly unlikely that a bogus configuration
was introduced only there, thus a backport would be preferable, or maybe
only the err_code should be turned to ERR_WARN to remain tolerant for
existing versions.

4 weeks agoBUG/MINOR: cfgparse-listen: always reject extra arguments to "persist"
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
BUG/MINOR: cfgparse-listen: always reject extra arguments to "persist"

The check for extra arguments was placed inside the "if" block which
parses "persist rdp-cookie(<name>)", so "persist rdp-cookie foo" was
silently accepted. Let's move it to the beginning of the keyword's
processing so that it covers all the cases.

This must NOT be backported since it makes the config parser stricter.

4 weeks agoBUG/MINOR: cfgparse-global: reject extra arguments to numa-cpu-mapping and anonkey
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
BUG/MINOR: cfgparse-global: reject extra arguments to numa-cpu-mapping and anonkey

Both keywords silently ignored anything passed after their expected
argument, contrary to all the other keywords of the section which
properly check for extra args. Let's just fix this.

This must NOT be backported since it makes the config parser stricter.

4 weeks agoBUG/MINOR: cfgparse-listen: fix quoting of the condition conflict warning
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
BUG/MINOR: cfgparse-listen: fix quoting of the condition conflict warning

The 10 places which report the message returned by warnif_cond_conflicts()
use "'%s.\n'" as a format, so the closing quote is emitted after the \n and
ends up alone at the beginning of the next line:

  [WARNING] : parsing [x.cfg:4] : 'acl 'a' will never match ...
  '

Let's just move the quote before the LF and even the trailing '.'.

This may be backported to all supported versions.

4 weeks agoBUG/MINOR: cfgparse-listen: fix the argument checked by "email-alert"
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
BUG/MINOR: cfgparse-listen: fix the argument checked by "email-alert"

The "from", "mailers", "myhostname" and "to" options of "email-alert"
each check that a value was passed, but they test args[1], which is the
option's own name instead fo args[2]. As a consequence an empty args[2]
was accepted. The same copy-paste mistake made the "level" option error
handling report the option name instead of the wrong value.

This may be backported to all supported versions.

4 weeks agoBUG/MINOR: cfgparse-listen: release the previous description of the proxy
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
BUG/MINOR: cfgparse-listen: release the previous description of the proxy

The proxy's "description" keyword did not release the previous value
before assigning the new one, so the first one was leaked when the
keyword was used twice, e.g. once in a defaults section and once in the
proxy. Note that the global section's "description" does it properly.

This may be backported to all supported versions.

4 weeks agoBUG/MINOR: cfgparse-global: add missing calloc() check for the global description
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
BUG/MINOR: cfgparse-global: add missing calloc() check for the global description

The global "description" keyword passes the result of calloc() directly to
snprintf() without checking it, resulting in a crash instead of an error
message if the allocation fails.

This may be backported to all supported versions.

4 weeks agoBUG/MINOR: cfgparse-global: add missing gotos to error label in max-spread-checks
Willy Tarreau [Tue, 15 Sep 2026 14:37:58 +0000 (16:37 +0200)]
BUG/MINOR: cfgparse-global: add missing gotos to error label in max-spread-checks

The three error paths of the parse_time_err() call in the
"max-spread-checks" keyword only set the error code without leaving the
block, so that global.max_spread_checks was assigned an uninitialized
value right after the error was reported. No impact but clearly what
could report a build warning or a report from a static analyser. Let's
just add the missing gotos like everywhere else.

This may be backported to all supported versions.

4 weeks agoBUG/MAJOR: cfgparse: properly check userlist section when scanning keywords
Willy Tarreau [Fri, 9 Oct 2026 06:52:44 +0000 (08:52 +0200)]
BUG/MAJOR: cfgparse: properly check userlist section when scanning keywords

In cfg_parse_users(), we scan all config keywords and only want to
evaluate those whose section is CFG_USERLIST. However, the section
is checked as a bit field instead of an equality, and CFG_USERLIST
is 3, which is CFG_GLOBAL|CFG_LISTEN, meaning that we can call a
wrong parser for keywords "user" or "group" when they are registered
in one of these sections. This is what randomly happens when the
global "group" keyword is refactored to have its own parser, depending
on build options the linker, it may randomly be found before or after
the userlist's and cause startup failures if the global section's
keyword parser is called from the userlist parser, such as in this
regtest that only fails with ASAN on the CI when the config only has
"group" set in a userlist section:

  config : parsing [cfg:18] : cannot find group id for 'admins' (0:Success)

This was introduced in 3.4 with commit c17ed69bf3 ("MINOR: cfgparse:
Refactor "userlist" parser to print it in -dKall operation") so it must
be backported there.

If a config works it's OK, but this was still marked major because
there's simply no workaround for a failing binary where it only
depends on build options. It could happen for example if other
keywords are added via Lua or patches.

4 weeks agoBUG/MEDIUM: h3: fix incorrect DATA size block on DEM FULL
Amaury Denoyelle [Thu, 8 Oct 2026 17:24:20 +0000 (19:24 +0200)]
BUG/MEDIUM: h3: fix incorrect DATA size block on DEM FULL

The following patch fixes h3_data_to_htx() to ensure the correct value
is returned in case of buffer wrapper.

  bcdbc399d34f82a21c49162f203f2ff16b0aed79
  BUG/MEDIUM: h3: fix end-of-input report on DATA wrapping

However, this introduces a regression when HTX buffer is smaller than
the copied block. In this case, QCS is flagged with QC_SF_DEM_FULL, but
early goto skip the increment on <htx_sent> total value.

This causes corrupted transfers with H3 POST larger than a bufsize
resulting in a larger payload. This is caused by duplicated data blocks
when copy resumes after hitting QC_SF_DEM_FULL condition.

Fix this by ensuring <htx_sent> is always updated before the early goto
path. Intent of the above fix is kept as well as <ret> still serves as a
comparison for QC_SF_DEM_FULL / EOM cases.

This must be backported along the above patch, planned up to 2.6 (after
a period of observation).

4 weeks agoMINOR: doc: elaborate on diagnostic mode behavior
Lukas Tribus [Thu, 8 Oct 2026 17:16:21 +0000 (17:16 +0000)]
MINOR: doc: elaborate on diagnostic mode behavior

Commit 7b01a8dbdd("MINOR: global: define diagnostic mode of execution")
in 2.4-dev15 introduced diagnostic mode for config warnings.

It was initially designed to be ignored by "zero-warning" mode
(-dW) and documented accordingly.

Since commit ced4148401 ("BUG/MINOR: diag: always show the version
before dumping a diag warning") in 3.0-dev3 this changed, and
zero-warning mode combined with diagnostic messages stop haproxy from
starting. This appears to be a unintended consequence in ced4148401.

Since commit 7d63efa5f5 ("MINOR: errors: add ha_diag_notice() to
report diag-level notifications") there is a distinction between
what should not interact with zero-warning mode (ha_diag_notice()),
and the previous behavior with ha_diag_warning().

Explain the that some diagnostic message interact with zero-warning
and that diagnostic message will be backported, as such diagnostic
mode must only be used interactively.

Should be backported to stable trees.

Co-Authored-By: Willy Tarreau <w@1wt.eu>
4 weeks agoMEDIUM: h3: do not close conn on unaligned non DATA frame
Amaury Denoyelle [Thu, 8 Oct 2026 07:34:34 +0000 (09:34 +0200)]
MEDIUM: h3: do not close conn on unaligned non DATA frame

Until now, H3 parser rejected a non aligned frame, except for DATA. The
whole connection was closed with error H3_EXCESSIVE_LOAD, which can be
seen as a medium defect given that the client did not produce anything
invalid or even suspect.

In fact, most frame parsers are already able to support wrapping
content, as they deal with a struct buffer representation. This is also
the case for QPACK uni streams. The real limitation is for HEADERS frame
which rely on qpack_decode_fs() which can only work on aligned content.

This patch removes the connection closure on unaligned content. For H3
HEADERS frames, the buffer content is realigned by performing two
memmove() calls into a temporarily allocated alternative area. The
current parsing iteration will continue in this new aligned storage.

A future optimisation would be to support wrapping content in
qpack_decode_fs() to remove realign operation.

This can be considered as a bug, as previous state prevents to handle
valid H3 content. As no user complains about it, it may be better to
refrain from backporting the patch, unless there is some explicit demand
about it.

Note that original Rx buffer is not realigned, as this would mangle the
underlying MUX ncbuf storage. An alternative approach was implemented
first, performing the realign at the MUX level, however it was probably
less efficient as it required to copy the whole ncbuf. For the record,
this patch is attached here with its original message.

---
Subject: [PATCH] MEDIUM: quic/h3: support parsing of unaligned non DATA H3
 frame

Until now, H3 parser was not able to parse a non DATA frame which wraps
in QCS Rx buffer. When this happened, the whole connection was closed
with error H3_EXCESSIVE_LOAD, which can be seen as a medium defect given
that the client did not produce anything invalid or even suspect.

This patch finally implements a method to deal with unaligned non DATA
H3 frames. When such frame is detected by H3 parser, rcv_buf callback is
interrupted. MUX will react to a null returned value and detect that the
buffer data is wrapping. In this case, the new function
qcs_realign_rx_data() is used to perform the realign operation using
b_slow_realign() helper. Callback rcv_buf is then reinvoked immediately.

This can be considered as a bug, as previous state prevents to handle
valid H3 content. As no user complains about it, it may be better to
refrain from backporting the patch, unless there is some explicit demand
about it.
---
 src/h3.c       |  8 ++------
 src/mux_quic.c | 35 ++++++++++++++++++++++++++++++++---
 2 files changed, 34 insertions(+), 9 deletions(-)

diff --git a/src/h3.c b/src/h3.c
index 4750ff69f..1ed4a4d8b 100644
--- a/src/h3.c
+++ b/src/h3.c
@@ -2068,13 +2068,9 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
  goto err;
  }

- /* TODO extend parser to support the realignment of a frame. */
  if (b_head(b) + b_data(b) > b_wrap(b)) {
- TRACE_ERROR("cannot parse unaligned data frame", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
- qcc_set_error(qcs->qcc, H3_ERR_EXCESSIVE_LOAD, 1,
-               muxc_tevt_type_other_err);
- qcc_report_glitch(qcs->qcc, 1);
- goto err;
+ TRACE_PROTO("pause parsing on unaligned payload", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
+ break;
  }

  /* Only parse full HTTP/3 frames. */
diff --git a/src/mux_quic.c b/src/mux_quic.c
index de443a568..70a6cad2d 100644
--- a/src/mux_quic.c
+++ b/src/mux_quic.c
@@ -1255,6 +1255,24 @@ static inline struct buffer qcs_b_dup(const struct qc_stream_rxbuf *b)
  }
 }

+/* Realign buffer content in <rxbuf>. This should be called when parsing is
+ * blocked due to wrapping content.
+ */
+static void qcs_realign_rx_data(struct qc_stream_rxbuf *rxbuf)
+{
+ struct ncbuf *ncb = &rxbuf->ncb;
+ struct buffer old;
+
+ /* Convert ncbuf to a full buffer type. This forces b_slow_realign()
+  * will copy every non contiguous blocks plus first ncb block header.
+  */
+ old = b_make(ncb_orig(ncb), ncb->size, ncb->head, ncb->size);
+ b_slow_realign(&old, trash.area, 0);
+
+ /* ncb area now realigned - ensure head is resetted as well. */
+ ncb->head = 0;
+}
+
 /* Transfer data into <qcs> stream <rxbuf> current Rx buffer from its directly
  * following buffer. This is useful when parsing was interrupted due to partial
  * data. If following buffer does not exists, nothing is done.
@@ -1508,9 +1526,20 @@ static int qcc_decode_qcs(struct qcc *qcc, struct qcs *qcs)
   * at a Rx buffer boundary. Try to realign data if possible and
   * restart decoding.
   */
- if (!ret && rxbuf && !(qcs->flags & QC_SF_DEM_FULL) &&
-     qcs->rx.offset + ncb_data(&rxbuf->ncb, 0) == rxbuf->off_end) {
- if (!qcs_transfer_rx_data(qcs, rxbuf)) {
+ if (!ret && rxbuf && !(qcs->flags & QC_SF_DEM_FULL)) {
+ int need_restart = 0;
+
+ if (ncb_head(&rxbuf->ncb) + ncb_data(&rxbuf->ncb, 0) > ncb_wrap(&rxbuf->ncb)) {
+ qcs_realign_rx_data(rxbuf);
+ need_restart = 1;
+ }
+
+ if (qcs->rx.offset + ncb_data(&rxbuf->ncb, 0) == rxbuf->off_end) {
+ if (!qcs_transfer_rx_data(qcs, rxbuf))
+ need_restart = 1;
+ }
+
+ if (need_restart) {
  TRACE_DEVEL("restart parsing after data realignment", QMUX_EV_QCS_RECV, qcc->conn, qcs);
  goto restart;
  }
--
2.56.0

4 weeks agoBUG/MEDIUM: h3: fix end-of-input report on DATA wrapping
Amaury Denoyelle [Wed, 7 Oct 2026 13:31:40 +0000 (15:31 +0200)]
BUG/MEDIUM: h3: fix end-of-input report on DATA wrapping

A received H3 DATA frame may wrap in the buffer. Function
h3_data_to_htx() will parse it in two pass in this case, calling
htx_add_data() twice.

A double bug was present in case of such parsing as second call of
htx_add_data() return value was immediately added in <htx_sent>. This
could cause first check to activate QC_SF_DEM_FULL to be ignored. This
is a minor issue, as MUX would immediately reinvoke h3_rcv_buf(), with
the flag set this time.

The second bug is more important : comparison for HTX EOM reporting was
invalid in case of a two pass parsing. Most of the time, this result in
EOM not reported, causing the stream layer to report a stream
interruption with state CL--.

Fix this by using an intermediary return value for the second
htx_add_data() invokation. This value is only added to <htx_sent> once
necessary checks are performed. It is important to keep h3_data_to_htx()
to still return the total value of HTX added data to avoid parser desync
issue.

This must be backported up to 2.6, after a period of observation.

5 weeks agoBUG/MINOR: ssl: Fix potential NULL-deref in "dump ssl cert" with AWS-LC
Remi Tricot-Le Breton [Mon, 5 Oct 2026 13:37:36 +0000 (15:37 +0200)]
BUG/MINOR: ssl: Fix potential NULL-deref in "dump ssl cert" with AWS-LC

In some corner cases we could have a public certificate with no private
key stored in a crt-store. In such a case, when trying to dump the
contents of the store entry via the "dump ssl cert" CLI command, the
'PEM_write_bio_PrivateKey' was always called without checking for a NULL
pointer.
This poses no problems with OpenSSL libraries which already check the
pointer but on some other libs, AWS-LC for instance, the NULL pointer is
dereferenced without check.
The only other use of this function is in acme code where the function
can never be called with a NULL 'key' pointer.

This patch can be backported up to 3.2.

5 weeks agoBUG/MEDIUM: mux_quic: notify stream on RESET_STREAM reception
Amaury Denoyelle [Tue, 29 Sep 2026 09:27:41 +0000 (11:27 +0200)]
BUG/MEDIUM: mux_quic: notify stream on RESET_STREAM reception

Always notify upper stream layer when a RESET_STREAM is received. This
is necessary as reset will be reported during rcv_buf callback.

Without this patch, reset won't be reported if stream is already
subscribed. This may result in a stream freeze, until its close on
timeout.

This should be backported up to 2.8, after a period of observation.

5 weeks agoBUG/MINOR: h3: reject RS/SS for QPACK uni streams
Amaury Denoyelle [Tue, 29 Sep 2026 10:05:41 +0000 (12:05 +0200)]
BUG/MINOR: h3: reject RS/SS for QPACK uni streams

A H3 peer must not close its qpack encoder/decoder stream. This is
already detected when a stream frame with FIN is received.

This patch extends the detection to RESET_STREAM frames. A similar error
H3_CLOSED_CRITICAL_STREAM is emitted.

This must be backported up to 2.8.

5 weeks agoBUG/MINOR: h3: close connection on GOAWAY frame too long
Amaury Denoyelle [Tue, 29 Sep 2026 09:48:54 +0000 (11:48 +0200)]
BUG/MINOR: h3: close connection on GOAWAY frame too long

GOAWAY frame contains a single stream ID varint. When decoding it,
ensure its length match the announced value in frame header. If not the
case, the connection must be closed with code FRAME_ERROR.

This must be backported up to 3.4.

5 weeks agoBUG/MINOR: h3: reject frames with truncated header
Amaury Denoyelle [Thu, 24 Sep 2026 16:06:02 +0000 (18:06 +0200)]
BUG/MINOR: h3: reject frames with truncated header

The following patch ensures that H3 frames with a truncated payload are
now properly rejected with H3_FRAME_ERROR.

  86a4ebc761a278838e8cb06f3a292282ba704c65
  BUG/MAJOR: h3: reject H3 truncated frames

However, this fix is not complete as it did not detect H3 frame
truncated at the header boundary. This happens when one of the two
fields varint cannot be decoded and FIN is reported as well.

This patch corrects this by reporting a connection error H3_FRAME_ERROR
in this condition. This allows to quickly teardown the connection
instead of waiting for the stream timeout without any error.

This must be backported to all stable releases.

5 weeks agoBUG/MINOR: mux_quic: account data in conn fctl on early detach
Amaury Denoyelle [Tue, 29 Sep 2026 14:39:17 +0000 (16:39 +0200)]
BUG/MINOR: mux_quic: account data in conn fctl on early detach

The previous patch accounts unconsumed data when QCS is freed before
being remotely closed. The objective is to ensure to limit the
inconsistency in conn flow control between client and server.

In fact, this patch is incomplete as peer consumed level cannot be known
before receiving its final size. Thus, discrepancies in connection flow
control may still appear over time. The proper method is to delay QCS
release until remote close is performed. However, this is a significant
change which will be implemented in a future patch.

This must be backported up to 2.8, after a period of observation

5 weeks agoBUG/MEDIUM: mux_quic: account discarded data in connection flow control
Amaury Denoyelle [Wed, 23 Sep 2026 13:34:28 +0000 (15:34 +0200)]
BUG/MEDIUM: mux_quic: account discarded data in connection flow control

Read data not yet consumed by the upper stream layer are discarded when
a RESET_STREAM is received. This patch implements their accounting in
the connection flow control.

This feature is mandatory as it could result in discrepancies between
each peers for the measured connection flow control. If this grows too
big, the sender may be indefinitely blocked, forcing the opening of a
new connection.

This patch introduces a new function qcc_account_conn_fctl(), which is
used to factorize the code already present during STREAM frames
handling. This is now also called for RESET_STREAM frames.

This must be backported up to 2.8, after a period of observation.

5 weeks agoBUG/MINOR: mux_quic: report conn error on RESET_STREAM fctl violation
Amaury Denoyelle [Wed, 23 Sep 2026 13:28:29 +0000 (15:28 +0200)]
BUG/MINOR: mux_quic: report conn error on RESET_STREAM fctl violation

When a RESET_STREAM is received, its announced final size must be taken
into account in the connection flow control.

This patch is a prerequisite prior to implement this accounting. It adds
the mandatory check of the final size field to ensure it does not
violate either the stream or the connection flow control.

In case of invalid value, the connection is closed with
FLOW_CONTROL_ERROR.

This must be backported up to 2.8, after a period of observation.

5 weeks agoBUG/MINOR: mux_quic: extend FINAL_SIZE_ERROR detection on stream
Amaury Denoyelle [Tue, 29 Sep 2026 09:49:13 +0000 (11:49 +0200)]
BUG/MINOR: mux_quic: extend FINAL_SIZE_ERROR detection on stream

On STREAM reception, a frame is rejected if final size is known but a
frame which exceeds it is received. Connection is closed with
FINAL_SIZE_ERROR code. This is also the case for a frame with FIN set
but shorter than the already received maximum offset.

This patch rewrites the condition so that the second check on frame with
FIN is always performed. Indeed, it does not depend on the known final
size status. A similar test is already performed as expected in the
RESET_STREAM frame handler.

This must be backported up to 2.6.

5 weeks agoBUG/MINOR: mux_quic: prevent purgeable QCS lingering
Amaury Denoyelle [Wed, 23 Sep 2026 12:31:37 +0000 (14:31 +0200)]
BUG/MINOR: mux_quic: prevent purgeable QCS lingering

Ensure QCC tasklet is woken up when a QCS is inserted in the purge list.
The objective is to prevent keeping QCS for too long in it.

Changes are only necessary when running outside of QCC tasklet. Thus new
wakeup are only inserted in receive handlers to avoid unnecessary
tasklet execution.

Prior to this patch, a purgeable QCS could have been kept virtually
indefinitely. This probably did not really happen as QCC tasklet is
frequently woken after frames handling.

This should be backported up to 3.2, after a period of observation.

5 weeks agoBUG/MAJOR: mux-quic: prevent crash on QCS already set for purging
Amaury Denoyelle [Mon, 5 Oct 2026 08:57:22 +0000 (10:57 +0200)]
BUG/MAJOR: mux-quic: prevent crash on QCS already set for purging

QCC connection has a purge list member. It contains QCS instances
considered as completed, whiches will be freed during the next tasklet
run. Such QCS are reported via qcs_is_completed() helper function.

QCS member <el_send> is used for purge list storage. It is also used for
send list, however an inactive QCS should not be present in the latter.
To enforce this rule, a BUG_ON() is present before purge list insertion
in qcs_close_remote(). This would also be triggered if QCS was already
inserted in the purge list before remote closing.

This BUG_ON() may be triggered when a QCS is scheduled for purge while
it still contains stom data to read. When the final read is performed,
crash will occur in qcs_close_remote(). This happens with detached QCS
only, as they are considered as completed even if only closed locally.

To prevent this situation, add an early return in qcs_close_remote() :
if QCS is already considered completed, do not change its state to fully
closed. As stated above, this only occur for detached QCS instances. The
main objective is to prevent the BUG_ON() crash while still preventing
double insert. As an added value, it guarantees that QCS state remains
constant once scheduled for purge.

Thanks to Brendon C. <bren@bren.win> for his detailled report on the
issue.

This must be backported up to 3.2, after a period of observation.

5 weeks agoMINOR: ssl: accept integer group IDs in curve2name
William Lallemand [Tue, 6 Oct 2026 13:26:21 +0000 (15:26 +0200)]
MINOR: ssl: accept integer group IDs in curve2name

Allow curve2name to convert integer TLS Supported Group identifiers
in addition to curve names. Register the converter with SMP_T_ANY to
preserve the input type, and accept only integer and string samples.

Return unknown IDs, or IDs without a name in the requested convention,
as decimal strings. Preserve the existing fallback for string inputs.

Document binary group extraction and extend regression coverage for
integer IDs, missing names, unknown IDs and values outside the TLS
identifier range. The build and converter regression test pass.

5 weeks agoMINOR: ssl: add curve2name converter
William Lallemand [Tue, 6 Oct 2026 12:59:43 +0000 (14:59 +0200)]
MINOR: ssl: add curve2name converter

Add a string converter to normalize curve names to IANA, ANSI X9.62
or NIST notation. Match both the requested format and input names
case-insensitively, and resolve old ecdh_* aliases to canonical names.

Keep the original string unchanged when no curve matches or the
requested naming convention has no corresponding name. The converter
uses the curve table independently of the SSL library's available NIDs.

Document the supported formats and add regression coverage for curve
aliases, mixed-case formats and unchanged-string fallbacks. The build
and all three SSL curve regression tests pass.

5 weeks agoMINOR: ssl: complete registered group names and library NID mappings
William Lallemand [Tue, 6 Oct 2026 12:41:52 +0000 (14:41 +0200)]
MINOR: ssl: complete registered group names and library NID mappings

Add the missing IANA groups for SM2, ML-KEM and hybrid key exchanges.
Provide both AWS-LC and OpenSSL NID spellings for standalone ML-KEM
and use AWS-LC NIDs for the supported hybrid and Kyber draft groups.
Undefined NID macros remain unavailable through the existing V macro.

The mappings were checked with OpenSSL 3.5, OpenSSL 4 development
headers and AWS-LC 4.2.

5 weeks agoMINOR: ssl: skip unavailable NIDs in curves2nid lookup
William Lallemand [Tue, 6 Oct 2026 12:22:22 +0000 (14:22 +0200)]
MINOR: ssl: skip unavailable NIDs in curves2nid lookup

Curve table entries with NIDs undefined by the SSL library remain at -1
after initialization. Skip these entries during name lookup so an
unavailable mapping does not hide a later valid mapping for the same
name. This allows the table to contain both OpenSSL and AWS-LC NID
spellings without conditional compilation.

5 weeks agoMINOR: ssl: split curve names into ANSI, IANA and NIST columns
William Lallemand [Tue, 6 Oct 2026 11:59:03 +0000 (13:59 +0200)]
MINOR: ssl: split curve names into ANSI, IANA and NIST columns

Store ANSI X9.62 aliases separately from IANA and NIST names,
consolidating the duplicate P-192 and P-256 entries. Make curves2nid()
search all three columns and keep curveid2str() returning the existing
TLS group name.

5 weeks agoBUG/MINOR: ssl: use standard names for X25519 and X448 groups
William Lallemand [Tue, 6 Oct 2026 11:51:45 +0000 (13:51 +0200)]
BUG/MINOR: ssl: use standard names for X25519 and X448 groups

The curve table uses ecdh_x25519 and ecdh_x448, which are OpenSSL TLS1.3
draft11 name labels rather than the released group names registered at
IANA. As a result, SSL traces print those labels and curves2nid() cannot
resolve x25519 or x448 even when the corresponding NID is available.

Use x25519 and x448 as the primary names. Keep the old ecdh_* strings
as aliases at the end of the table so existing name-to-NID lookups
continue to work. The aliases must follow the standard names because
curveid2str() returns the first entry matching a group identifier.

curveid2str() is only used in the traces so there's no fonctionnal
changes.

This should be backported to 3.3 and 3.4, which contain the curve table
introduced by d799a1b3b (MINOR: ssl: Add curve id to curve name table
and mapping functions).

5 weeks agoDOC: ssl: clarify the NID-based curve fetch branch
William Lallemand [Mon, 5 Oct 2026 16:27:55 +0000 (16:27 +0000)]
DOC: ssl: clarify the NID-based curve fetch branch

Document that the SSL_get_negotiated_group() path is used for OpenSSL
before 3.2 and AWS-LC API 35 or later. This only adds a comment and
does not change the fetch behavior.

5 weeks agoBUG/MINOR: ssl: don't uppercase the native group name
William Lallemand [Mon, 5 Oct 2026 16:21:29 +0000 (16:21 +0000)]
BUG/MINOR: ssl: don't uppercase the native group name

The change to SSL_get0_group_name() assumed that its output differed
from SSL_get_negotiated_group() followed by OBJ_nid2sn() only in
case. This is true for x25519/X25519 and x448/X448, but cannot be
generalized to other groups.

For P-256, the APIs return secp256r1 and prime256v1 respectively:
these are aliases, not a case difference. For P-384 and P-521, both
APIs already return secp384r1 and secp521r1, so uppercasing actually
introduces a mismatch. It also changes native mixed-case hybrid names
such as SecP256r1MLKEM768.

Moreover, SSL_get0_group_name() returns a library-owned string shared
through the SSL_CTX. Uppercasing it in place modifies OpenSSL state
and risks concurrent access to that string.

Remove the uppercase conversion and return the native name unchanged.

This should not be backported as it would could break matching of the
curves.

5 weeks agoBUG/MEDIUM: listener: Consider maxconn before waking listeners
Olivier Houchard [Tue, 6 Oct 2026 12:42:47 +0000 (14:42 +0200)]
BUG/MEDIUM: listener: Consider maxconn before waking listeners

When dequeueing listeners in dequeue_all_listeners() and
dequeue_proxy_listeners(), consider if we reached maxconn before doing
so.
If we do not, with enough thread groups, we may enter a state in which we
dequeue listeners, try again to accept more connections, but that would
fail, as maxconn would have been reached again by a different thread
group, and we'd end up being stuck for long enough for the watchdog to
trigger.

This should be backported up to 2.8.

This was submitted by Bruno Henc in github issue #3525.

5 weeks agoCI: github: move wolfSSL 5.7.0 testing to a nightly workflow
William Lallemand [Tue, 6 Oct 2026 10:28:00 +0000 (12:28 +0200)]
CI: github: move wolfSSL 5.7.0 testing to a nightly workflow

Remove wolfSSL 5.7.0 from the push-triggered VTest matrix. Update the
standalone wolfSSL workflow to test that version nightly instead of
building git-master weekly.

The weekly git-master worflow was not working anymore for months.

5 weeks agoCI: github: cache QuicTLS builds
William Lallemand [Tue, 6 Oct 2026 09:22:26 +0000 (11:22 +0200)]
CI: github: cache QuicTLS builds

Resolve the QuicTLS ref to a commit SHA when generating the VTest
matrix and include it in the SSL build inputs and cache key. Keep job
labels readable by showing the ref and an eight-character SHA.

Fetch and check out the resolved commit in build-ssl.sh while retaining
QUICTLS_VERSION for the OpenSSL 1.1.1 build target selection. Preserve
the existing download behavior when QUICTLS_COMMIT is unset.

Enable SSL caching for QuicTLS.

5 weeks agoCI: github: test the latest AWS-LC release on development branches
William Lallemand [Tue, 6 Oct 2026 08:41:30 +0000 (10:41 +0200)]
CI: github: test the latest AWS-LC release on development branches

Replace the AWS-LC 1.39.0 pin with latest-release discovery using the
existing determine_latest_aws_lc() helper. Add it alongside the other
latest SSL libraries in the development-only matrix list.

Remove the separate AWS-LC workflow now that the VTest matrix covers
the latest release and both LTS branches.

5 weeks agoCI: github: test AWS-LC 3.x and 4.x LTS branches
William Lallemand [Tue, 6 Oct 2026 08:35:28 +0000 (10:35 +0200)]
CI: github: test AWS-LC 3.x and 4.x LTS branches

Add the fips-2024-09-27 (3.x) and fips-2025-09-12-lts (4.x) AWS-LC
branches to the VTest matrix for GCC and Clang, including QUIC support.

Resolve each branch head to a commit SHA when generating the matrix
and download the corresponding immutable archive in build-ssl.sh.
Read the AWS-LC version from that commit's header to display readable
job names such as ssl=aws_lc=4.2.0+37341efe.

Generate the SSL cache key from both the job name and SSL source,
retaining the full commit SHA even though the display uses only eight
characters. Builds are reused until the branch advances, and distinct
commits cannot share a cache entry because of a short SHA collision.

Validated branch resolution and version extraction against GitHub,
readable GCC and Clang jobs in development and stable matrices, commit
and tag archive URLs, cache reuse, shell syntax, and git diff --check.

5 weeks agoBUG/MEDIUM: ssl: crt-list curves ignored with AWS-LC
Frederic Lecaille [Tue, 6 Oct 2026 07:59:44 +0000 (09:59 +0200)]
BUG/MEDIUM: ssl: crt-list curves ignored with AWS-LC

SSL_new() copies the curves of the SSL_CTX into the SSL object, and
SSL_set_SSL_CTX() does not update this copy while the handshake uses it.
The "curves" and "ecdhe" options of a crt-list line were thus silently
ignored: a client could negotiate a curve excluded by the certificate as
long as the bind line allowed it.

The ClientHello callback used by AWS-LC now sets them again on the SSL
object after the context switch. The value is picked in the same order
as when the SSL_CTX is prepared, so the curves of the bind line still
win over the <ecdhe> of the crt-list line.

The curves setter is inefficient here: it parses the configuration
string for each connection.

This adds the ssl_crt-list_curves reg-tests for TCP and QUIC.

Tested with AWS-LC 3.6.0 (LTS) and 4.2.0. The curves regression tests
pass over TCP and QUIC with both versions.
The TLSv1.2, TLSv1.3 and QUIC cipher regression tests also pass.

Thank you to @ygkat for having reported this issue in GH #3500

This should be backported to all stable versions. Note the code is in
ssl_sock.c before 3.1.

Co-authored-by: William Lallemand <wlallemand@haproxy.com>
5 weeks agoBUG/MEDIUM: ssl: crt-list ciphers ignored with AWS-LC
Frederic Lecaille [Tue, 6 Oct 2026 07:46:11 +0000 (09:46 +0200)]
BUG/MEDIUM: ssl: crt-list ciphers ignored with AWS-LC

AWS-LC's SSL_new() copies the cipher lists of the SSL_CTX into the SSL
object, and SSL_set_SSL_CTX() does not update this copy while the server
handshake uses it. The "ciphers" and "ciphersuites" options of a
crt-list line were thus silently ignored: a client could negotiate a cipher
excluded by the certificate as long as the bind line allowed it. The same
configuration built against OpenSSL rejects such a connection.

The SNI callback now sets both lists again on the SSL object after the
context switch when built with AWS-LC API versions greater than 30.
API version 30 (AWS-LC 3.x) uses the selected SSL_CTX cipher lists
directly and does not need this workaround.

These setters are inefficient here: they parse the cipher configuration
strings for each connection.

This adds the ssl_crt-list_ciphers reg-test for TLSv1.2 and the
ssl_crt-list_ciphersuites ones for TLSv1.3, over TCP and QUIC.

Tested with AWS-LC LTS versions 3.6.0 and 4.2.0.

Thank you to @ygkat for having reported this issue in GH #3500

This should be backported as far as 3.0. Note the code is in ssl_sock.c
before 3.1.

Co-authored-by: William Lallemand <wlallemand@haproxy.com>
5 weeks agoBUG/MINOR: ssl: fix trace crash on BE conn release in io cb
Amaury Denoyelle [Tue, 6 Oct 2026 07:45:37 +0000 (09:45 +0200)]
BUG/MINOR: ssl: fix trace crash on BE conn release in io cb

Since 3.3, handling of idle connections in io handler for muxes/SSL has
been extended. On io completion, connection is not reinserted anymore in
the server list if it is in maintenance mode. Also, private connections
are need race protection as they may be purged : on completion, a
connection is reinserted in its parent session. This operation is
failible as it may require an allocation.

In SSL io tasklet, if a BE connection is not reinserted in its idle pool
for one of the two reasons described above, it is freed instead via MUX
destroy callback. This causes a crash if traces are activated as conn
pointer is not updated.

Fix this by ensuring conn pointer is set to NULL after destroy
invokation.

This must be backported up to 3.3.

5 weeks agoBUG/MINOR: ssl: fix trace crash on FE conn release in io cb
Amaury Denoyelle [Mon, 5 Oct 2026 15:42:18 +0000 (17:42 +0200)]
BUG/MINOR: ssl: fix trace crash on FE conn release in io cb

The following patch has added an output parameter in conn_create_mux()
to report if the connection has been destroyed. This can happen for
frontend connection only.

In case the connection is destroyed, tasklet is set to NULL as expected
to avoid returning an invalid pointer. However, conn pointer is left
unchanged. This leads to a crash if traces are activated. The proper fix
is to also set conn to NULL.

This must be backported up to 3.2.

5 weeks agoBUG/MINOR: hq-interop: prevent double EOM on request with standalone FIN
Amaury Denoyelle [Tue, 6 Oct 2026 08:37:06 +0000 (10:37 +0200)]
BUG/MINOR: hq-interop: prevent double EOM on request with standalone FIN

Previously, HTX EOM was unconditionnaly set during HTTP/0.9 to HTX
request conversion. This was valid as content was only parsed when all
STREAM were received.

However, this has changed in 3.2 with support for incomplete request and
standalone FIN in HTTP/0.9 parser. The objective is to better test a
mechanism close to HTTP/3 parser. But unconditionnal EOM was not
adjusted. Thus, EOM would be set two times per message if FIN is
transmitted via a dedicated empty STREAM frame.

Previously, this was silently ignored by stream layer. However, this is
not case anymore since conversion of HTX EOM from a message level to a
block level flag. A new BUG_ON() added in htx_add_blk() would report
incorrect blocks found after EOM : this may be triggered in case the
second EOM is reported through an EOT block.

Fix this only setting EOM if fin is set during HTTP/0.9 request parsing.
Handling of standalone FIN is already supported here so there is no need
for more changes. Now, HTTP/0.9 parser can now considered even more
closer to HTTP/3.

This should be backported up to 3.2.

5 weeks agoDEBUG: support reading set-dumpable from HAPROXY_FORCE_DUMPABLE
Willy Tarreau [Tue, 6 Oct 2026 08:10:02 +0000 (10:10 +0200)]
DEBUG: support reading set-dumpable from HAPROXY_FORCE_DUMPABLE

When debugging regtests, it is super complicated to get a core to be
dumped. It often requires to place a set-dumpable directive in the
config, but when it's generated it's really not convenient. A better
approach in this case is to read the value from an environment variable.

That's what this patch does, by accepting to preset the set-dumpable
options from the contents of HAPROXY_FORCE_DUMPABLE. It's a bit more
relaxed, though, as it accepts "off" to stay disabled, "libs" to just
load the libs, and anything else to enable.

The config and management docs were updated. This could be backported
to older versions if found helpful.

5 weeks agoBUILD: ssl: avoid mixed declarations in the AWS-LC curve fetch
William Lallemand [Mon, 5 Oct 2026 13:04:29 +0000 (13:04 +0000)]
BUILD: ssl: avoid mixed declarations in the AWS-LC curve fetch

The AWS-LC API 30-34 curve fetch declares the group variable after
statements, which fails builds using -Werror=declaration-after-statement.

Move the declaration to the beginning of smp_fetch_ssl_fc_ec() and keep
the assignment in the AWS-LC branch. Mark the variable __maybe_unused
for builds using other SSL libraries.

This fix must be backported wherever commit e671cb696d ("MINOR: ssl:
support curve fetches with AWS-LC API 30") was backported.

5 weeks agoMINOR: ssl: support curve fetches with AWS-LC API 30
William Lallemand [Mon, 5 Oct 2026 12:45:35 +0000 (14:45 +0200)]
MINOR: ssl: support curve fetches with AWS-LC API 30

Enable ssl_fc_curve and ssl_bc_curve with AWS-LC API 30-34, including
AWS-LC 3.0.0, using SSL_get_group_id() and SSL_get_group_name() instead
of SSL_get_negotiated_group(), which requires API 35.

Map NIST curve group IDs to OpenSSL short names to keep the output
consistent with newer AWS-LC versions. Preserve native names for other
groups, including post-quantum and hybrid groups.

Update the compatibility documentation and enable the curve regression
tests for AWS-LC API 30.

5 weeks agoDOC: config: document new "loggers" section and relevant uses
Aurelien DARRAGON [Fri, 2 Oct 2026 13:56:03 +0000 (15:56 +0200)]
DOC: config: document new "loggers" section and relevant uses

loggers (plural) section may be used to defined a group of log
directives within a single section that can be used later in the
config as a replacement of "log" directive to specify log target(s),
for instance in a proxy section to inherit from existing loggers or
directly on a do-log action to send generated log to specific endpoints
only.

5 weeks agoMINOR: log: support 'to' parameter fo do-log action
Aurelien DARRAGON [Fri, 2 Oct 2026 07:15:32 +0000 (09:15 +0200)]
MINOR: log: support 'to' parameter fo do-log action

Similar to the "to" argument for "log" directive, the optional "to"
parameter allows to specify a list of comma separater loggers section
names to be used as log destination for the logs produce by the do-log
action.

However, while the "to" argument from "log" directive adds up loggers
to existing loggers of the proxy, the do-log "to" argument actually
replaces the loggers configured on the proxy! Indeed, the ultimate
goal is to be able to filter which loggers sections should receive
do-log logs, which by default are sent to all loggers from the proxy.

5 weeks agoMINOR: log: support explicit <loggers> list for do_log_ctx()
Aurelien DARRAGON [Fri, 2 Oct 2026 09:10:03 +0000 (11:10 +0200)]
MINOR: log: support explicit <loggers> list for do_log_ctx()

This way we can provide our own list of loggers to override the default
one (defaults to sess->fe->loggers when <logger> is NULL).

5 weeks agoMINOR: log: support 'to' parameter for "log" directive
Aurelien DARRAGON [Thu, 1 Oct 2026 09:03:03 +0000 (11:03 +0200)]
MINOR: log: support 'to' parameter for "log" directive

"to" is like "global", this is a special (optional) argument to be used
alone without ordinary log's arguments. It expects a comma separated list
of "loggers" section names which indicates which loggers section to inherit
loggers from. (each loggers section defines a number of "log" directive,
called logger).