]> git.ipfire.org Git - thirdparty/haproxy.git/log
thirdparty/haproxy.git
3 years agoBUG/MINOR: filters: Always set FLT_END analyser when CF_FLT_ANALYZE flag is set
Christopher Faulet [Fri, 13 Aug 2021 12:00:46 +0000 (14:00 +0200)] 
BUG/MINOR: filters: Always set FLT_END analyser when CF_FLT_ANALYZE flag is set

CF_FLT_ANALYZE flags may be set before the FLT_END analyser. Thus if an error is
triggered in the mean time, this may block the stream and prevent it to be
released. It is indeed a problem only for the response channel because the
response analysers may be skipped on early errors.

So, to prevent any issue, depending on the code path, the FLT_END analyser is
systematically set when the CF_FLT_ANALYZE flag is set.

This patch must be backported in all stable branches.

3 years agoMINOR: proxy: disable warnings for internal proxies
William Lallemand [Fri, 13 Aug 2021 13:21:12 +0000 (15:21 +0200)] 
MINOR: proxy: disable warnings for internal proxies

The internal proxies should be part of the proxies list, because of
this, the check_config_validity() fonction could emit warnings about
these proxies.

This patch disables 3 startup warnings for internal proxies:

- "has no 'bind' directive" (this one was already ignored for the CLI
frontend, but we made it generic instead)
- "missing timeouts"
- "log format ignored"

3 years agoMINOR: cli: delare the CLI frontend as an internal proxy
William Lallemand [Fri, 13 Aug 2021 13:31:33 +0000 (15:31 +0200)] 
MINOR: cli: delare the CLI frontend as an internal proxy

Declare the CLI frontend as an internal proxy so we can check the
PR_CAP_INT flag instead of the global.fe_cli pointer for generic use
cases.

3 years agoBUG/MEDIUM: cfgcheck: verify existing log-forward listeners during config check
Emeric Brun [Fri, 13 Aug 2021 07:32:50 +0000 (09:32 +0200)] 
BUG/MEDIUM: cfgcheck: verify existing log-forward listeners during config check

User reported that the config check returns an error with the message:
"Configuration file has no error but will not start (no listener) => exit(2)."
if the configuration present only a log-forward section with bind or dgram-bind
listeners but no listen/backend nor peer sections.

The process checked if there was 'peers' section avalaible with
an internal frontend (and so a listener) or a 'listen/backend'
section not disabled with at least one configured listener (into the
global proxies_list). Since the log-forward proxies appear in a
different list, they were not checked.

This patch adds a lookup on the 'log-forward' proxies list to check
if one of them presents a listener and is not disabled. And
this is done only if there was no available listener found into
'listen/backend' sections.

I have also studied how to re-work this check considering the 'listeners'
counter used after startup/init to keep the same algo and avoid further
mistakes but currently this counter seems increased during config parsing
and if a proxy is disabled, decreased during startup/init which is done
after the current config check. So the fix still not rely on this
counter.

This patch should fix the github issue #1346

This patch should be backported as far as 2.3 (so on branches
including the "log-forward" feature)

3 years agoBUG/MINOR: lua: Properly catch alloc errors when parsing lua filter directives
Christopher Faulet [Fri, 13 Aug 2021 06:33:57 +0000 (08:33 +0200)] 
BUG/MINOR: lua: Properly catch alloc errors when parsing lua filter directives

When a lua filter declaration is parsed, some allocation errors were not
properly handled. In addition, we must be sure the filter identifier is defined
in lua to duplicate it when the filter configuration is filled.

This patch fix a defect reported in the issue #1347. It only concerns
2.5-dev. No backport needed.

3 years agoBUG/MINOR: lua: Properly check negative offset in Channel/HttpMessage functions
Christopher Faulet [Fri, 13 Aug 2021 06:11:00 +0000 (08:11 +0200)] 
BUG/MINOR: lua: Properly check negative offset in Channel/HttpMessage functions

In Channel and HTTPMessage classes, several functions uses an offset that
may be negative to start from the end of incoming data. But, after
calculation, the offset must never be negative. However, there is a bug
because of a bad cast to unsigned when "input + offset" is performed. The
result must be a signed integer.

This patch should fix most of defects reported in the issue #1347. It only
affects 2.5-dev. No backport needed.

3 years agoDOC: config: Fix 'http-response send-spoe-group' documentation
Christopher Faulet [Thu, 12 Aug 2021 07:32:07 +0000 (09:32 +0200)] 
DOC: config: Fix 'http-response send-spoe-group' documentation

Arguments were missing in the rule heading. This patch may be backported as
far as 2.0.

3 years agoMINOR: filters/lua: Support the HTTP filtering from filters written in lua
Christopher Faulet [Wed, 26 Feb 2020 16:15:48 +0000 (17:15 +0100)] 
MINOR: filters/lua: Support the HTTP filtering from filters written in lua

Now an HTTPMessage class is available to manipulate HTTP message from a filter
it is possible to bind HTTP filters callback function on lua functions. Thus,
following methods may now be defined by a lua filter:

  * Filter:http_headers(txn, http_msg)
  * Filter:http_payload(txn, http_msg, offset, len)
  * Filter:http_end(txn, http_msg)

http_headers() and http_end() may return one of the constant filter.CONTINUE,
filter.WAIT or filter.ERROR. If nothing is returned, filter.CONTINUE is used as
the default value. On its side, http_payload() may return the amount of data to
forward. If nothing is returned, all incoming data are forwarded.

For now, these functions are not allowed to yield because this interferes with
the filter workflow.

3 years agoMINOR: filters/lua: Add request and response HTTP messages in the lua TXN
Christopher Faulet [Wed, 26 Feb 2020 16:14:08 +0000 (17:14 +0100)] 
MINOR: filters/lua: Add request and response HTTP messages in the lua TXN

When a lua TXN is created from a filter context, the request and the response
HTTP message objects are accessible from ".http_req" and ".http_res" fields. For
an HTTP proxy, these objects are always defined. Otherwise, for a TCP proxy, no
object is created and nil is used instead. From any other context (action or
sample fetch), these fields don't exist.

3 years agoMEDIUM: filters/lua: Add HTTPMessage class to help HTTP filtering
Christopher Faulet [Wed, 26 Feb 2020 15:57:19 +0000 (16:57 +0100)] 
MEDIUM: filters/lua: Add HTTPMessage class to help HTTP filtering

This new class exposes methods to manipulate HTTP messages from a filter
written in lua. Like for the HTTP class, there is a bunch of methods to
manipulate the message headers. But there are also methods to manipulate the
message payload. This part is similar to what is available in the Channel
class. Thus the payload can be duplicated, erased, modified or
forwarded. For now, only DATA blocks can be retrieved and modified because
the current API is limited. No HTTPMessage method is able to yield. Those
manipulating the headers are always called on messages containing all the
headers, so there is no reason to yield. Those manipulating the payload are
called from the http_payload filters callback function where yielding is
forbidden.

When an HTTPMessage object is instantiated, the underlying Channel object
can be retrieved via the ".channel" field.

For now this class is not used because the HTTP filtering is not supported
yet. It will be the purpose of another commit.

There is no documentation for now.

3 years agoMEDIUM: filters/lua: Support declaration of some filter callback functions in lua
Christopher Faulet [Wed, 26 Feb 2020 14:03:09 +0000 (15:03 +0100)] 
MEDIUM: filters/lua: Support declaration of some filter callback functions in lua

It is now possible to write some filter callback functions in lua. All
filter callbacks are not supported yet but the mechanism to call them is now
in place. Following method may be defined in the Lua filter class to be
bound on filter callbacks:

  * Filter:start_analyse(txn, chn)
  * Filter:end_analyse(txn, chn)
  * Filter:tcp_payload(txn, chn, offset, length)

hlua_filter_callback() function is responsible to call the good lua function
depending on the filter callback function. Using some flags it is possible
to allow a lua call to yield or not, to retrieve a return value or not, and
to specify if a channel or an http message must be passed as second
argument. For now, the HTTP part has not been added yet. It is also possible
to add extra argument adding them on the stack before the call.

3 new functions are exposed by the global object "filter". The first one,
filter.wake_time(ms_delay), to set the wake_time when a Lua callback
function yields (if allowed). The two others,
filter.register_data_filter(filter, chn) and
filter.unregister_data_filter(filter, chn), to enable or disable the data
filtering on a channel for a specific lua filter instance.

start_analyse() and end_analyse() may return one of the constant
filter.CONTINUE, filter.WAIT or filter.ERROR. If nothing is returned,
filter.CONTINUE is used as the default value. On its side, tcp_payload() may
return the amount of data to forward. If nothing is returned, all incoming
data are forwarded.

For now, these functions are not allowed to yield because this interferes
with the filter workflow.

Here is a simple example :

    MyFilter = {}
    MyFilter.id = "My Lua filter"
    MyFilter.flags = filter.FLT_CFG_FL_HTX
    MyFilter.__index = MyFilter

    function MyFilter:new()
        flt = {}
        setmetatable(flt, MyFilter)
        flt.req_len = 0
        flt.res_len = 0
        return flt
     end

    function MyFilter:start_analyze(txn, chn)
        filter.register_data_filter(self, chn)
    end

    function MyFilter:end_analyze(txn, chn)
        print("<Total> request: "..self.req_len.." - response: "..self.res_len)
    end

    function MyFilter:tcp_payload(txn, chn)
        offset = chn:ouput()
len    = chn:input()
        if chn:is_resp() then
            self.res_len = self.res_len + len
            print("<TCP:Response> offset: "..offset.." - length: "..len)
        else
            self.req_len = self.req_len + len
            print("<TCP:Request> offset: "..offset.." - length: "..len)
        end
    end

3 years agoMEDIUM: filters/lua: Be prepared to filter TCP payloads
Christopher Faulet [Mon, 9 Aug 2021 08:22:46 +0000 (10:22 +0200)] 
MEDIUM: filters/lua: Be prepared to filter TCP payloads

For filters written in lua, the tcp payloads will be filtered using methods
exposed by the Channel class. So the corrsponding C binding functions must
be prepared to process payload in a filter context and not only in an action
context.

The main change is the offset where to start to process data in the channel
buffer, and the length of these data. For an action, all input data are
considered. But for a filter, it depends on what the filter is allow to
forward when the tcp_payload callback function is called. It depends on
previous calls but also on other filters.

In addition, when the payload is modified by a lua filter, its context must
be updated. Note also that channel functions cannot yield when called from a
filter context.

For now, it is not possible to define callbacks to filter data and the
documentation has not been updated.

3 years agoMINOR: lua: Add flags on the lua TXN to know the execution context
Christopher Faulet [Fri, 6 Aug 2021 14:29:41 +0000 (16:29 +0200)] 
MINOR: lua: Add flags on the lua TXN to know the execution context

A lua TXN can be created when a sample fetch, an action or a filter callback
function is executed. A flag is now used to track the execute context.
Respectively, HLUA_TXN_SMP_CTX, HLUA_TXN_ACT_CTX and HLUA_TXN_FLT_CTX. The
filter flag is not used for now.

3 years agoMINOR: lua: Add a function to get a filter attached to a channel class
Christopher Faulet [Tue, 25 Feb 2020 14:21:02 +0000 (15:21 +0100)] 
MINOR: lua: Add a function to get a filter attached to a channel class

For now, there is no support for filters written in lua. So this function,
if called, will always return NULL. But when it will be called in a filter
context, it will return the filter structure attached to a channel
class. This function is also responsible to set the offset of data that may
be processed and the length of these data. When called outside a filter
context (so from an action), the offset is the input data position and the
length is the input data length. From a filter, the offset and the length of
data that may be filtered are retrieved the filter context.

3 years agoMEDIUM: filters/lua: Add support for dummy filters written in lua
Christopher Faulet [Mon, 31 May 2021 06:54:04 +0000 (08:54 +0200)] 
MEDIUM: filters/lua: Add support for dummy filters written in lua

It is now possible to write dummy filters in lua. Only the basis to declare
such filters has been added for now. There is no way to declare callbacks to
filter anything. Lua filters are for now empty nutshells.

To do so, core.register_filter() must be called, with 3 arguments, the
filter's name (as it appears in HAProxy config), the lua class that will be
used to instantiate filters and a function to parse arguments passed on the
filter line in HAProxy configuration file. The lua filter class must at
least define the method new(), without any extra args, to create new
instances when streams are created. If this method is not found, the filter
will be ignored.

Here is a template to declare a new Lua filter:

    // haproxy.conf
    global
        lua-load /path/to/my-filter.lua
        ...

    frontend fe
        ...
        filter lua.my-lua-filter arg1 arg2 arg3
        filter lua.my-lua-filter arg4 arg5

    // my-filter.lua
    MyFilter = {}
    MyFilter.id = "My Lua filter"          -- the filter ID (optional)
    MyFilter.flags = filter.FLT_CFG_FL_HTX -- process HTX streams (optional)
    MyFilter.__index = MyFilter

    function MyFilter:new()
        flt = {}
        setmetatable(flt, MyFilter)
        -- Set any flt fields. self.args can be used
flt.args = self.args

        return flt -- The new instance of Myfilter
    end

    core.register_filter("my-lua-filter", MyFilter, function(filter, args)
        -- process <args>, an array of strings. For instance:
        filter.args = args
        return filter
    end)

In this example, 2 filters are declared using the same lua class. The
parsing function is called for both, with its own copy of the lua class. So
each filter will be unique.

The global object "filter" exposes some constants and flags, and later some
functions, to help writting filters in lua.

Internally, when a lua filter is instantiated (so when new() method is
called), 2 lua contexts are created, one for the request channel and another
for the response channel. It is a prerequisite to let some callbacks yield
on one side independently on the other one.

There is no documentation for now.

3 years agoDOC: Improve the lua documentation
Christopher Faulet [Wed, 11 Aug 2021 08:14:30 +0000 (10:14 +0200)] 
DOC: Improve the lua documentation

The ReST syntax is used for note and warning blocks.

3 years agoMEDIUM: lua: Improve/revisit the lua api to manipulate channels
Christopher Faulet [Fri, 6 Aug 2021 14:02:36 +0000 (16:02 +0200)] 
MEDIUM: lua: Improve/revisit the lua api to manipulate channels

First of all, following functions are now considered deprecated:

  * Channel:dup()
  * Channel:get()
  * Channel:getline()
  * Channel:get_in_len()
  * Cahnnel:get_out_len()

It is just informative, there is no warning and functions may still be
used. Howver it is recommended to use new functions. New functions are more
flexible and use a better naming pattern. In addition, the same names will
be used in the http_msg class to manipulate http messages from lua filters.

The new API is:

  * Channel:data()
  * Channel:line()
  * Channel:append()
  * Channel:prepend()
  * Channel:insert()
  * Channel:remove()
  * Channel:set()
  * Channel:input()
  * Channel:output()
  * Channel:send()
  * Channel:forward()
  * Channel:is_resp()
  * Channel:is_full()
  * Channel:may_recv()

The lua documentation was updated accordingly.

3 years agoMEDIUM: lua: Process buffer data using an offset and a length
Christopher Faulet [Fri, 6 Aug 2021 11:49:54 +0000 (13:49 +0200)] 
MEDIUM: lua: Process buffer data using an offset and a length

The main change is that following functions will now process channel's data
using an offset and a length:

  * hlua_channel_dup_yield()
  * hlua_channel_get_yield()
  * hlua_channel_getline_yield()
  * hlua_channel_append_yield()
  * hlua_channel_set()
  * hlua_channel_send_yield()
  * hlua_channel_forward_yield()

So for now, the offset is always the input data position and the length is
the input data length. But with the support for filters, from a filter
context, these values will be relative to the filter.

To make all processing clearer, the function _hlua_channel_dup() has been
updated and _hlua_channel_dupline(), _hlua_channel_insert() and
_hlua_channel_delete() have been added.

This patch is mandatory to allow the support of the filters written in lua.

3 years agoMINOR: lua: Add a function to get a reference on a table in the stack
Christopher Faulet [Tue, 25 Feb 2020 09:20:04 +0000 (10:20 +0100)] 
MINOR: lua: Add a function to get a reference on a table in the stack

The hlua_checktable() function may now be used to create and return a
reference on a table in stack, given its position. This function ensures it
is really a table and throws an exception if not.

This patch is mandatory to allow the support of the filters written in lua.

3 years agoMINOR: filters/lua: Release filters before the lua context
Christopher Faulet [Mon, 24 Feb 2020 15:26:55 +0000 (16:26 +0100)] 
MINOR: filters/lua: Release filters before the lua context

This patch is mandatory to allow the support of the filters written in lua.

3 years agoBUG/MINOR: lua: Don't yield in channel.append() and channel.set()
Christopher Faulet [Fri, 6 Aug 2021 07:59:49 +0000 (09:59 +0200)] 
BUG/MINOR: lua: Don't yield in channel.append() and channel.set()

Lua functions to set or append data to the input part of a channel must not
yield because new data may be received while the lua script is suspended. So
adding data to the input part in several passes is highly unpredicatble and
may be interleaved with received data.

Note that if necessary, it is still possible to suspend a lua action by
returning act.YIELD. This way the whole action will be reexecuted later
because of I/O events or a timer. Another solution is to call core.yield().

This bug affects all stable versions. So, it may be backported. But it is
probably not necessary because nobody notice it till now.

3 years agoBUG/MINOR: lua: Yield in channel functions only if lua context can yield
Christopher Faulet [Thu, 5 Aug 2021 09:58:37 +0000 (11:58 +0200)] 
BUG/MINOR: lua: Yield in channel functions only if lua context can yield

When a script is executed, it is not always allowed to yield. Lua sample
fetches and converters cannot yield. For lua actions, it depends on the
context. When called from tcp content ruleset, an action may yield until the
expiration of the inspect-delay timeout. From http rulesets, yield is not
possible.

Thus, when channel functions (dup, get, append, send...) are called, instead
of yielding when it is not allowed and triggering an error, we just give
up. In this case, some functions do nothing (dup, append...), some others
just interrupt the in-progress job (send, forward...). But, because these
functions don't yield anymore when it is not allowed, the script regains the
control and can continue its execution.

This patch depends on "MINOR: lua: Add a flag on lua context to know the
yield capability at run time". Both may be backported in all stable
versions. However, because nobody notice this bug till now, it is probably
not necessary, excepted if someone ask for it.

3 years agoMINOR: lua: Add a flag on lua context to know the yield capability at run time
Christopher Faulet [Wed, 4 Aug 2021 15:58:21 +0000 (17:58 +0200)] 
MINOR: lua: Add a flag on lua context to know the yield capability at run time

When a script is executed, a flag is used to allow it to yield. An error is
returned if a lua function yield, explicitly or not. But there is no way to
get this capability in C functions. So there is no way to choose to yield or
not depending on this capability.

To fill this gap, the flag HLUA_NOYIELD is introduced and added on the lua
context if the current script execution is not authorized to yield. Macros
to set, clear and test this flags are also added.

This feature will be usefull to fix some bugs in lua actions execution.

3 years agoBUG/MINOR: stream: Don't release a stream if FLT_END is still registered
Christopher Faulet [Wed, 13 Nov 2019 10:12:32 +0000 (11:12 +0100)] 
BUG/MINOR: stream: Don't release a stream if FLT_END is still registered

When at least one filter is registered on a stream, the FLT_END analyzer is
called on both direction when all other analyzers have finished their
processing. During this step, filters may release any allocated elements if
necessary. So it is important to not skip it.

Unfortunately, if both stream interfaces are closed, it is possible to not
wait the end of this analyzer. It is possible to be in this situation if a
filter must wait and prevents the analyzer completion. To fix the bug, we
now wait FLT_END analyzer is no longer registered on both direction before
releasing the stream.

This patch may be backported as far as 1.7, but AFAIK, no filter is affected
by this bug. So the backport seems to be optional for now. In any case, it
should remain under observation for some weeks first.

3 years agoBUG/MINOR: tcpcheck: Properly detect pending HTTP data in output buffer
Christopher Faulet [Wed, 11 Aug 2021 13:46:29 +0000 (15:46 +0200)] 
BUG/MINOR: tcpcheck: Properly detect pending HTTP data in output buffer

In tcpcheck_eval_send(), the condition to detect there are still pending
data in the output buffer is buggy. Presence of raw data must be tested for
TCP connection only. But a condition on the connection was missing to be
sure it is not an HTX connection.

This patch must be backported as far as 2.2.

3 years agoMINOR: channel: remove an htx block from a channel
William Lallemand [Wed, 11 Aug 2021 11:40:14 +0000 (13:40 +0200)] 
MINOR: channel: remove an htx block from a channel

co_htx_remove_blk() implements a way to remove an htx block from a
channel buffer and update the channel output.

3 years agoBUG/MINOR: buffer: fix buffer_dump() formatting
William Lallemand [Mon, 9 Aug 2021 17:37:16 +0000 (19:37 +0200)] 
BUG/MINOR: buffer: fix buffer_dump() formatting

The formatting of the buffer_dump() output must be calculated using the
relative counter, not the absolute one, or everything will be broken if
the <from> variable is not a multiple of 16.

Could be backported in all maintained versions.

3 years agoBUG/MEDIUM: server: support both check/agent-check on a dynamic instance
Amaury Denoyelle [Tue, 10 Aug 2021 14:24:36 +0000 (16:24 +0200)] 
BUG/MEDIUM: server: support both check/agent-check on a dynamic instance

A static server is able to support simultaneously both health chech and
agent-check. Adjust the dynamic server CLI handlers to also support this
configuration.

This should not be backported, unless dynamic server checks are
backported.

3 years agoBUG/MEDIUM: check: fix leak on agent-check purge
Amaury Denoyelle [Tue, 10 Aug 2021 14:23:49 +0000 (16:23 +0200)] 
BUG/MEDIUM: check: fix leak on agent-check purge

There is currently a leak on agent-check for dynamic servers. When
deleted, the check rules and vars are not liberated. This leak grows
each time a dynamic server with agent-check is deleted.

Replace the manual purge code by a free_check invocation which
centralizes all the details on check cleaning.

There is no leak for health check because in this case the proxy is the
owner of the check vars and rules.

This should not be backported, unless dynamic server checks are
backported.

3 years agoBUG/MINOR: check: fix leak on add dynamic server with agent-check error
Amaury Denoyelle [Tue, 10 Aug 2021 14:22:51 +0000 (16:22 +0200)] 
BUG/MINOR: check: fix leak on add dynamic server with agent-check error

If an error occured during a dynamic server creation, free_check is used
to liberate a possible agent-check. However, this does not free
associated vars and rules associated as this is done on another function
named deinit_srv_agent_check.

To simplify the check free and avoid a leak, move free vars/rules in
free_check. This is valid because deinit_srv_agent_check also uses
free_check.

This operation is done only for an agent-check because for a health
check, the proxy instance is the owner of check vars/rules.

This should not be backported, unless dynamic server checks are
backported.

3 years agoBUG/MINOR: check: do not reset check flags on purge
Amaury Denoyelle [Tue, 10 Aug 2021 14:21:55 +0000 (16:21 +0200)] 
BUG/MINOR: check: do not reset check flags on purge

Do not reset check flags when setting CHK_ST_PURGE.

Currently, this change has no impact. However, it is semantically wrong
to clear important flags such as CHK_ST_AGENT on purge.

Furthermore, this change will become mandatoy for a future fix to
properly free agent checks on dynamic servers removal. For this, it will
be needed to differentiate health/agent-check on purge via CHK_ST_AGENT
to properly free agent checks.

This must not be backported unless dynamic servers checks are
backported.

3 years agoADMIN: dyncookie: implement a simple dynamic cookie calculator
Willy Tarreau [Wed, 11 Aug 2021 11:54:52 +0000 (13:54 +0200)] 
ADMIN: dyncookie: implement a simple dynamic cookie calculator

This utility can be useful to figure what cookie value a server will
have based on the secret, its IP and its port.

3 years agoBUG/MINOR: server: do not use refcount in free_server in stopping mode
Amaury Denoyelle [Mon, 9 Aug 2021 13:08:54 +0000 (15:08 +0200)] 
BUG/MINOR: server: do not use refcount in free_server in stopping mode

Currently there is a leak at process shutdown with dynamic servers with
check/agent-check activated. Check purges are not executed on process
stopping, so the server is not liberated due to its refcount.

The solution is simply to ignore the refcount on process stopping mode
and free the server on the first free_server invocation.

This should not be backported, unless dynamic server checks are
backported. In this case, the following commit must be backported first.
  7afa5c1843521ec3be7549592d2b38ccc9d68b73
  MINOR: global: define MODE_STOPPING

3 years agoMINOR: global: define MODE_STOPPING
Amaury Denoyelle [Mon, 9 Aug 2021 13:02:56 +0000 (15:02 +0200)] 
MINOR: global: define MODE_STOPPING

Define a new mode MODE_STOPPING. It is used to indicate that the process
is in the stopping stage and no event loop runs anymore.

3 years agoBUG/MINOR: check: test if server is not null in purge
Amaury Denoyelle [Mon, 9 Aug 2021 13:09:17 +0000 (15:09 +0200)] 
BUG/MINOR: check: test if server is not null in purge

Test if server is not null before using free_server in the check purge
operation. Currently, the null server scenario should not occured as
purge is used with refcounted dynamic servers. However, this might not
be always the case if purge is use in the future in other cases; thus
the test is useful for extensibility.

No need to backport, unless dynamic server checks are backported.

This has been reported through a coverity report in github issue #1343.

3 years agoCI: travis-ci: temporarily disable arm64 builds
Ilya Shipitsin [Tue, 3 Aug 2021 09:54:09 +0000 (14:54 +0500)] 
CI: travis-ci: temporarily disable arm64 builds

few recent builds failed with "gcc: fatal error: Killed signal
terminated program cc1", let us disable arm64 builds until investigation

3 years agoREGTESTS: server: fix dynamic server with checks test
Amaury Denoyelle [Fri, 6 Aug 2021 13:34:04 +0000 (15:34 +0200)] 
REGTESTS: server: fix dynamic server with checks test

Add a missing 'rxreq' statement in first server. Without it the test is
unstable. The issue is frequent when running with one thread only.

This should fix github issue #1342.

3 years agoMINOR: doc: specify ulimit-n usage for dynamic servers
Amaury Denoyelle [Fri, 6 Aug 2021 08:25:32 +0000 (10:25 +0200)] 
MINOR: doc: specify ulimit-n usage for dynamic servers

Complete the documentation of the dynamic servers to warn about a
possible fd resource exhaustion when using a large number of them.

3 years agoREGTESTS: server: add dynamic check server test
Amaury Denoyelle [Thu, 29 Jul 2021 15:34:16 +0000 (17:34 +0200)] 
REGTESTS: server: add dynamic check server test

Write a regtest to validate check support by dynamic servers. Three
differents servers are added on various configuration :
- server OK
- server DOWN
- agent-check

3 years agoMEDIUM: server: implement agent check for dynamic servers
Amaury Denoyelle [Wed, 4 Aug 2021 09:33:14 +0000 (11:33 +0200)] 
MEDIUM: server: implement agent check for dynamic servers

This commit is the counterpart for agent check of
"MEDIUM: server: implement check for dynamic servers".

The "agent-check" keyword is enabled for dynamic servers. The agent
check must manually be activated via "enable agent" CLI. This can
enable the dynamic server if the agent response is "ready" without an
explicit "enable server" CLI.

3 years agoMEDIUM: server: implement check for dynamic servers
Amaury Denoyelle [Thu, 22 Jul 2021 14:04:59 +0000 (16:04 +0200)] 
MEDIUM: server: implement check for dynamic servers

Implement check support for dynamic servers. The "check" keyword is now
enabled for dynamic servers. If used, the server check is initialized
and the check task started in the "add server" CLI handler. The check is
explicitely disabled and must be manually activated via "enable health"
CLI handler.

The dynamic server refcount is incremented if a check is configured. On
"delete server" handler, the check is purged, which decrements the
refcount.

3 years agoMINOR: check: enable safe keywords for dynamic servers
Amaury Denoyelle [Fri, 23 Jul 2021 14:34:58 +0000 (16:34 +0200)] 
MINOR: check: enable safe keywords for dynamic servers

Implement a collection of keywords deemed safe and useful to dynamic
servers. The list of the supported keywords is :
- addr
- check-proto
- check-send-proxy
- check-via-socks4
- rise
- fall
- fastinter
- downinter
- port
- agent-addr
- agent-inter
- agent-port
- agent-send

3 years agoMEDIUM: check: implement check deletion for dynamic servers
Amaury Denoyelle [Thu, 29 Jul 2021 13:51:45 +0000 (15:51 +0200)] 
MEDIUM: check: implement check deletion for dynamic servers

Implement a mechanism to free a started check on runtime for dynamic
servers. A new function check_purge is created for this. The check task
will be marked for deletion and scheduled to properly close connection
elements and free the task/tasklet/buf_wait elements.

This function will be useful to delete a dynamic server wich checks.

3 years agoMINOR: server: implement a refcount for dynamic servers
Amaury Denoyelle [Mon, 2 Aug 2021 13:50:00 +0000 (15:50 +0200)] 
MINOR: server: implement a refcount for dynamic servers

It is necessary to have a refcount mechanism on dynamic servers to be
able to enable check support. Indeed, when deleting a dynamic server
with check activated, the check will be asynchronously removed. This is
mandatory to properly free the check resources in a thread-safe manner.
The server instance must be kept alive for this.

3 years agoMINOR: check: do not increment global maxsock at runtime
Amaury Denoyelle [Thu, 29 Jul 2021 13:39:43 +0000 (15:39 +0200)] 
MINOR: check: do not increment global maxsock at runtime

global maxsock is used to estimate a number of fd to reserve for
internal use, such as checks. It is incremented at startup with the info
from the config file.

Disable this incrementation in checks functions at runtime. First, it
currently serves no purpose to increment it after startup. Worse, it may
lead to out-of-bound accesse on the fdtab.

This will be useful to initiate checks for dynamic servers.

3 years agoMINOR: check: export check init functions
Amaury Denoyelle [Thu, 22 Jul 2021 14:04:40 +0000 (16:04 +0200)] 
MINOR: check: export check init functions

Remove static qualifier on init_srv_check, init_srv_agent_check and
start_check_task. These functions will be called in server.c for dynamic
servers with checks.

3 years agoMINOR: check: allocate default check ruleset for every backends
Amaury Denoyelle [Fri, 23 Jul 2021 13:46:46 +0000 (15:46 +0200)] 
MINOR: check: allocate default check ruleset for every backends

Allocate default tcp ruleset for every backend without explicit rules
defined, even if no server in the backend use check. This change is
required to implement checks for dynamic servers.

This allocation is done on check_config_validity. It must absolutely be
called before check_proxy_tcpcheck (called via post proxy check) which
allocate the implicit tcp connect rule.

3 years agoMINOR: server: initialize fields for dynamic server check
Amaury Denoyelle [Thu, 22 Jul 2021 14:03:36 +0000 (16:03 +0200)] 
MINOR: server: initialize fields for dynamic server check

Set default inter/rise/fall values for dynamic servers check/agent. This
is required because dynamic servers do not inherit from a
default-server.

3 years agoMEDIUM: task: implement tasklet kill
Amaury Denoyelle [Wed, 28 Jul 2021 14:12:57 +0000 (16:12 +0200)] 
MEDIUM: task: implement tasklet kill

Implement an equivalent of task_kill for tasklets. This function can be
used to request a tasklet deletion in a thread-safe way.

Currently this function is unused.

3 years agoMINOR: server: unmark deprecated on enable health/agent cli
Amaury Denoyelle [Tue, 3 Aug 2021 16:05:37 +0000 (18:05 +0200)] 
MINOR: server: unmark deprecated on enable health/agent cli

Remove the "DEPRECATED" marker on "enable/disable health/agent"
commands. Their purpose is to toggle the check/agent on a server.

These commands are still useful because their purpose is not covered by
the "set server" command. Most there was confusion with the commands
'set server health/agent', which in fact serves another goal.

Note that the indication "use 'set server' instead" has been added since
2016 on the commit
  2c04eda8b58636ad2ae44e42b1f50f3b5a24a642
  REORG: cli: move "{enable|disable} health" to server.c

and
  58d9cb7d22c1b0d8239543443131e3e3658375d0
  REORG: cli: move "{enable|disable} agent" to server.c

Besides, these commands will become required to enable check/agent on
dynamic servers which will be created with check disabled.

This should be backported up to 2.4.

3 years agoBUG/MEDIUM: spoe: Fix policy to close applets when SPOE connections are queued
Christopher Faulet [Mon, 2 Aug 2021 16:13:27 +0000 (18:13 +0200)] 
BUG/MEDIUM: spoe: Fix policy to close applets when SPOE connections are queued

It is the second part of the fix that should solve fairness issues with the
connections management inside the SPOE filter. Indeed, in multithreaded
mode, when the SPOE detects there are some connections in queue on a server,
it closes existing connections by releasing SPOE applets. It is mandatory
when a maxconn is set because few connections on a thread may prenvent new
connections establishment.

The first attempt to fix this bug (9e647e5af "BUG/MEDIUM: spoe: Kill applets
if there are pending connections and nbthread > 1") introduced a bug. In
pipelining mode, SPOE applets might be closed while some frames are pending
for the ACK reply. To fix the bug, in the processing stage, if there are
some connections in queue, only truly idle applets may process pending
requests. In this case, only one request at a time is processed. And at the
end of the processing stage, only truly idle applets may be released. It is
an empirical workaround, but it should be good enough to solve contention
issues when a low maxconn is set.

This patch should partely fix the issue #1340. It must be backported as far
as 2.0.

3 years agoBUG/MEDIUM: spoe: Create a SPOE applet if necessary when the last one is released
Christopher Faulet [Mon, 2 Aug 2021 15:53:56 +0000 (17:53 +0200)] 
BUG/MEDIUM: spoe: Create a SPOE applet if necessary when the last one is released

On a thread, when the last SPOE applet is released, if there are still
pending streams, a new one is created. Of course, HAproxy must not be
stopping. It is important to start a new applet in this case to not abort
in-progress jobs, especially when a maxconn is set. Because applets may be
closed to be fair with connections waiting for a free slot.

This patch should partely fix the issue #1340. It depends on the commit
"MINOR: spoe: Create a SPOE applet if necessary when the last one on a
thread is closed". Both must be backported as far as 2.0.

3 years agoMINOR: spoe: Add a pointer on the filter config in the spoe_agent structure
Christopher Faulet [Mon, 2 Aug 2021 15:51:01 +0000 (17:51 +0200)] 
MINOR: spoe: Add a pointer on the filter config in the spoe_agent structure

There was no way to access the SPOE filter configuration from the agent
object. However it could be handy to have it. And in fact, this will be
required to fix a bug.

3 years agoBUG/MINOR: server: update last_change on maint->ready transitions too
Willy Tarreau [Wed, 4 Aug 2021 17:35:13 +0000 (19:35 +0200)] 
BUG/MINOR: server: update last_change on maint->ready transitions too

Nenad noticed that when leaving maintenance, the servers' last_change
field was not updated. This is visible in the Status column of the stats
page in front of the state, as the cumuled time spent in the current state
is wrong, it starts from the last transition (typically ready->maint). In
addition, the backend's state was not updated either, because the down
transition is performed by set_backend_down() which also emits a log, and
it is this function which was extended to update the backend's last_change,
but it's not called for down->up transitions so that was not done.

The most visible (and unpleasant) effect of this bug is that it affects
slowstart so such a server could immediately restart with a significant
load ratio.

This should likely be backported to all stable releases.

3 years agoCLEANUP: fd: remove the now unneeded fd_mig_lock
Willy Tarreau [Tue, 3 Aug 2021 07:24:41 +0000 (09:24 +0200)] 
CLEANUP: fd: remove the now unneeded fd_mig_lock

This is not needed anymore since we don't use it when setting the running
mask anymore.

3 years agoCLEANUP: fd: remove the now unused fd_set_running()
Willy Tarreau [Tue, 3 Aug 2021 07:15:32 +0000 (09:15 +0200)] 
CLEANUP: fd: remove the now unused fd_set_running()

It was inlined inside fd_update_events() since it relies on a loop that
may return immediate failure codes.

3 years agoMAJOR: fd: get rid of the DWCAS when setting the running_mask
Willy Tarreau [Tue, 3 Aug 2021 07:04:32 +0000 (09:04 +0200)] 
MAJOR: fd: get rid of the DWCAS when setting the running_mask

Right now we're using a DWCAS to atomically set the running_mask while
being constrained by the thread_mask. This DWCAS is annoying because we
may seriously need it later when adding support for thread groups, for
checking that the running_mask applies to the correct group.

It turns out that the DWCAS is not strictly necessary because we never
need it to set the thread_mask based on the running_mask, only the other
way around. And in fact, the running_mask is always cleared alone, and
the thread_mask is changed alone as well. The running_mask is only
relevant to indicate a takeover when the thread_mask matches it. Any
bit set in running and not present in thread_mask indicates a transition
in progress.

As such, it is possible to re-arrange this by using a regular CAS around a
consistency check between running_mask and thread_mask in fd_update_events
and by making a CAS on running_mask then an atomic store on the thread_mask
in fd_takeover(). The only other case is fd_delete() but that one already
sets the running_mask before clearing the thread_mask, which is compatible
with the consistency check above.

This change has happily survived 10 billion takeovers on a 16-thread
machine at 800k requests/s.

The fd-migration doc was updated to reflect this change.

3 years agoMINOR: activity/fd: remove the dead_fd counter
Willy Tarreau [Tue, 3 Aug 2021 08:59:50 +0000 (10:59 +0200)] 
MINOR: activity/fd: remove the dead_fd counter

This one is set whenever an FD is reported by a poller with a null owner,
regardless of the thread_mask. It has become totally meaningless because
it only indicates a migrated FD that was not yet reassigned to a thread,
but as soon as a thread uses it, the status will change to skip_fd. Thus
there is no reason to distinguish between the two, it adds more confusion
than it helps. Let's simply drop it.

3 years agoBUG/MINOR: server: remove srv from px list on CLI 'add server' error
Amaury Denoyelle [Wed, 4 Aug 2021 09:20:05 +0000 (11:20 +0200)] 
BUG/MINOR: server: remove srv from px list on CLI 'add server' error

If an error occured during the CLI 'add server' handler, the newly
created server must be removed from the proxy list if already inserted.
Currently, this can happen on the extremely rare error during server id
generation if there is no id left.

The removal operation is not thread-safe, it must be conducted before
releasing the thread isolation.

This can be backported up to 2.4. Please note that dynamic server track
is not implemented in 2.4, so the release_server_track invocation must
be removed for the backport to prevent a compilation error.

3 years agoMEDIUM: servers: make the server deletion code run under full thread isolation
Willy Tarreau [Wed, 4 Aug 2021 12:42:37 +0000 (14:42 +0200)] 
MEDIUM: servers: make the server deletion code run under full thread isolation

In 2.4, runtime server deletion was brought by commit e558043e1 ("MINOR:
server: implement delete server cli command"). A comment remained in the
code about a theoretical race between the thread_isolate() call and another
thread being in the process of allocating memory before accessing the
server via a reference that was grabbed before the memory allocation,
since the thread_harmless_now()/thread_harmless_end() pair around mmap()
may have the effect of allowing cli_parse_delete_server() to proceed.

Now that the full thread isolation is available, let's update the code
to rely on this. Now it is guaranteed that competing threads will either
be in the poller or queued in front of thread_isolate_full().

This may be backported to 2.4 if any report of breakage suggests the bug
really exists, in which case the two following patches will also be
needed:
  MINOR: threads: make thread_release() not wait for other ones to complete
  MEDIUM: threads: add a stronger thread_isolate_full() call

3 years agoMEDIUM: threads: add a stronger thread_isolate_full() call
Willy Tarreau [Wed, 4 Aug 2021 09:44:17 +0000 (11:44 +0200)] 
MEDIUM: threads: add a stronger thread_isolate_full() call

The current principle of running under isolation was made to access
sensitive data while being certain that no other thread was using them
in parallel, without necessarily having to place locks everywhere. The
main use case are "show sess" and "show fd" which run over long chains
of pointers.

The thread_isolate() call relies on the "harmless" bit that indicates
for a given thread that it's not currently doing such sensitive things,
which is advertised using thread_harmless_now() and which ends usings
thread_harmless_end(), which also waits for possibly concurrent threads
to complete their work if they took this opportunity for starting
something tricky.

As some system calls were notoriously slow (e.g. mmap()), a bunch of
thread_harmless_now() / thread_harmless_end() were placed around them
to let waiting threads do their work while such other threads were not
able to modify memory contents.

But this is not sufficient for performing memory modifications. One such
example is the server deletion code. By modifying memory, it not only
requires that other threads are not playing with it, but are not either
in the process of touching it. The fact that a pool_alloc() or pool_free()
on some structure may call thread_harmless_now() and let another thread
start to release the same object's memory is not acceptable.

This patch introduces the concept of "idle threads". Threads entering
the polling loop are idle, as well as those that are waiting for all
others to become idle via the new function thread_isolate_full(). Once
thread_isolate_full() is granted, the thread is not idle anymore, and
it is released using thread_release() just like regular isolation. Its
users have to keep in mind that across this call nothing is granted as
another thread might have performed shared memory modifications. But
such users are extremely rare and are actually expecting this from their
peers as well.

Note that that in case of backport, this patch depends on previous patch:
  MINOR: threads: make thread_release() not wait for other ones to complete

3 years agoMINOR: threads: make thread_release() not wait for other ones to complete
Willy Tarreau [Wed, 4 Aug 2021 09:22:07 +0000 (11:22 +0200)] 
MINOR: threads: make thread_release() not wait for other ones to complete

The original intent of making thread_release() wait for other requesters to
proceed was more of a fairness trade, guaranteeing that a thread that was
granted an access to the CPU would be in turn giving back once its job is
done. But this is counter-productive as it forces such threads to spin
instead of going back to the poller, and it prevents us from implementing
multiple levels of guarantees, as a thread_release() call could spin
waiting for another requester to pass while that requester expects
stronger guarantees than the current thread may be able to offer.

Let's just remove that wait period and let the thread go back to the
poller, a-la "race to idle".

While in theory it could possibly slightly increase the perceived
latency of concurrent slow operations like "show fd" or "show sess",
it is not the case at all in tests, probably because the time needed
to reach the poller remains extremely low anyway.

3 years agoCLEANUP: thread: fix fantaisist indentation of thread_harmless_till_end()
Willy Tarreau [Wed, 4 Aug 2021 08:33:57 +0000 (10:33 +0200)] 
CLEANUP: thread: fix fantaisist indentation of thread_harmless_till_end()

Probably due to a copy-paste, there were two indent levels in this function
since its introduction in 1.9 by commit 60b639ccb ("MEDIUM: hathreads:
implement a more flexible rendez-vous point"). Let's fix this.

3 years agoBUG/MINOR: server: fix race on error path of 'add server' CLI if track
Amaury Denoyelle [Wed, 28 Jul 2021 08:06:52 +0000 (10:06 +0200)] 
BUG/MINOR: server: fix race on error path of 'add server' CLI if track

If an error occurs during a dynamic server creation with tracking, it
must be removed from the tracked list. This operation is not thread-safe
and thus must be conducted under the thread isolation.

Track support for dynamic servers has been introduced in this release.
This does not need to be backported.

3 years agoMINOR: stats: shows proxy in a stopped state
William Lallemand [Tue, 3 Aug 2021 11:18:11 +0000 (13:18 +0200)] 
MINOR: stats: shows proxy in a stopped state

Previous patch b5c0d65 ("MINOR: proxy: disabled takes a stopping and a
disabled state") allows us to set 2 states for a stopped or a disabled
proxy. With this patch we are now able to show the stats of all proxies
when the process is in a stopping states, not only when there is some
activity on a proxy.

This patch should fix issue #1307.

3 years agoMINOR: proxy: disabled takes a stopping and a disabled state
William Lallemand [Tue, 3 Aug 2021 09:58:03 +0000 (11:58 +0200)] 
MINOR: proxy: disabled takes a stopping and a disabled state

This patch splits the disabled state of a proxy into a PR_DISABLED and a
PR_STOPPED state.

The first one is set when the proxy is disabled in the configuration
file, and the second one is set upon a stop_proxy().

3 years agoMINOR: doc: rename conn_status in `option httsplog`
William Lallemand [Mon, 2 Aug 2021 08:57:49 +0000 (10:57 +0200)] 
MINOR: doc: rename conn_status in `option httsplog`

Rename the conn_status field by the real name of the sample fetch in the
`option httpslog` documentation.

3 years agoMINOR: log: rename 'dontloglegacyconnerr' to 'log-error-via-logformat'
William Lallemand [Mon, 2 Aug 2021 08:25:30 +0000 (10:25 +0200)] 
MINOR: log: rename 'dontloglegacyconnerr' to 'log-error-via-logformat'

Rename the 'dontloglegacyconnerr' option to 'log-error-via-logformat'
which is much more self-explanatory and readable.

Note: only legacy keywords don't use hyphens, it is recommended to
separate words with them in new keywords.

3 years ago[RELEASE] Released version 2.5-dev3 v2.5-dev3
Willy Tarreau [Sun, 1 Aug 2021 16:19:51 +0000 (18:19 +0200)] 
[RELEASE] Released version 2.5-dev3

Released version 2.5-dev3 with the following main changes :
    - BUG/MINOR: arg: free all args on make_arg_list()'s error path
    - BUG/MINOR: cfgcond: revisit the condition freeing mechanism to avoid a leak
    - MEDIUM: proxy: remove long-broken 'option http_proxy'
    - CLEANUP: http_ana: Remove now unused label from http_process_request()
    - MINOR: deinit: always deinit the init_mutex on failed initialization
    - BUG/MEDIUM: cfgcond: limit recursion level in the condition expression parser
    - BUG/MEDIUM: mworker: do not register an exit handler if exit is expected
    - BUG/MINOR: mworker: do not export HAPROXY_MWORKER_REEXEC across programs
    - BUILD/MINOR: memprof fix macOs build.
    - BUG/MEDIUM: ssl_sample: fix segfault for srv samples on invalid request
    - BUG/MINOR: stats: Add missing agent stats on servers
    - BUG/MINOR: check: fix the condition to validate a port-less server
    - BUILD: threads: fix pthread_mutex_unlock when !USE_THREAD
    - BUG/MINOR: resolvers: Use a null-terminated string to lookup in servers tree
    - MINOR: ssl: use __objt_* variant when retrieving counters
    - BUG/MINOR: systemd: must check the configuration using -Ws
    - BUG/MINOR: mux-h1: Obey dontlognull option for empty requests
    - BUG/MINOR: mux-h2: Obey dontlognull option during the preface
    - BUG/MINOR: mux-h1: Be sure to swap H1C to splice mode when rcv_pipe() is called
    - BUG/MEDIUM: mux-h2: Handle remaining read0 cases on partial frames
    - MINOR: proxy: rename PR_CAP_LUA to PR_CAP_INT
    - MINOR: mworker: the mworker CLI proxy is internal
    - MINOR: stats: don't output internal proxies (PR_CAP_INT)
    - CLEANUP: mworker: use the proxy helper functions in mworker_cli_proxy_create()
    - CLEANUP: mworker: PR_CAP already initialized with alloc_new_proxy()
    - BUG/MINOR: connection: Add missing error labels to conn_err_code_str
    - MINOR: connection: Add a connection error code sample fetch
    - MINOR: ssl: Enable error fetches in case of handshake error
    - MINOR: ssl: Add new ssl_fc_hsk_err sample fetch
    - MINOR: ssl: Define a default https log format
    - MEDIUM: connection: Add option to disable legacy error log
    - REGTESTS: ssl: Add tests for the connection and SSL error fetches
    - REGTESTS: ssl: ssl_errors.vtc does not work with old openssl version
    - BUG/MEDIUM: connection: close a rare race between idle conn close and takeover
    - BUG/MEDIUM: pollers: clear the sleeping bit after waking up, not before
    - BUG/MINOR: select: fix excess number of dead/skip reported
    - BUG/MINOR: poll: fix abnormally high skip_fd counter
    - BUG/MINOR: pollers: always program an update for migrated FDs
    - BUG/MINOR: fd: protect fd state harder against a concurrent takeover
    - DOC: internals: document the FD takeover process
    - MINOR: fd: update flags only once in fd_update_events()
    - MINOR: poll/epoll: move detection of RDHUP support earlier
    - REORG: fd: uninline fd_update_events()
    - MEDIUM: fd: rely more on fd_update_events() to detect changes
    - BUG/MINOR: freq_ctr: use stricter barriers between updates and readings
    - MEDIUM: atomic: simplify the atomic load/store/exchange operations
    - MEDIUM: atomic: relax the load/store barriers on x86_64
    - BUILD: opentracing: fixed build when using pkg-config utility

3 years agoBUILD: opentracing: fixed build when using pkg-config utility
Miroslav Zagorac [Thu, 29 Jul 2021 09:10:08 +0000 (11:10 +0200)] 
BUILD: opentracing: fixed build when using pkg-config utility

In case the OpenTracing C Wrapper library was installed as part of the
system (ie in a directory that pkg-config considers part of the system),
HAProxy building was not possible because in that case pkg-config did
not set the value of the OT_CFLAGS variable in the addon Makefile.

This resolves GitHub issue #1323.

3 years agoMEDIUM: atomic: relax the load/store barriers on x86_64
Willy Tarreau [Thu, 15 Jul 2021 14:02:47 +0000 (16:02 +0200)] 
MEDIUM: atomic: relax the load/store barriers on x86_64

The x86-tso model makes the load and store barriers unneeded for our
usage as long as they perform at least a compiler barrier: the CPU
will respect store ordering and store vs load ordering. It's thus
safe to remove the lfence and sfence which are normally needed only
to communicate with external devices. Let's keep the mfence though,
to make sure that reads of same memory location after writes report
the value from memory and not the one snooped from the write buffer
for too long.

An in-depth review of all use cases tends to indicate that this is
okay in the rest of the code. Some parts could be cleaned up to
use atomic stores and atomic loads instead of explicit barriers
though.

Doing this reliably increases the overall performance by about 2-2.5%
on a 8c-16t Xeon thanks to less frequent flushes (it's likely that the
biggest gain is in the MT lists which use them a lot, and that this
results in less cache line flushes).

3 years agoMEDIUM: atomic: simplify the atomic load/store/exchange operations
Willy Tarreau [Thu, 15 Jul 2021 12:48:21 +0000 (14:48 +0200)] 
MEDIUM: atomic: simplify the atomic load/store/exchange operations

The atomic_load/atomic_store/atomic_xchg operations were all forced to
__ATOMIC_SEQ_CST, which results in explicit store or even full barriers
even on x86-tso while we do not need them: we're not communicating with
external devices for example and are only interested in respecting the
proper ordering of loads and stores between each other.

These ones being rarely used, the emitted code on x86 remains almost the
same (barring a handful of locations). However they will allow to place
correct barriers at other places where atomics are accessed a bit lightly.

The patch is marked medium because we can never rule out the risk of some
bugs on more relaxed platforms due to the rest of the code.

3 years agoBUG/MINOR: freq_ctr: use stricter barriers between updates and readings
Willy Tarreau [Thu, 15 Jul 2021 13:45:44 +0000 (15:45 +0200)] 
BUG/MINOR: freq_ctr: use stricter barriers between updates and readings

update_freq_ctr_period() was using relaxed atomics without using barriers,
which usually works fine on x86 but not everywhere else. In addition, some
values were read without being enclosed by barriers, allowing the compiler
to possibly prefetch them a bit earlier. Finally, freq_ctr_total() was also
reading these without enough barriers. Let's make explicit use of atomic
loads and atomic stores to get rid of this situation. This required to
slightly rearrange the freq_ctr_total() loop, which could possibly slightly
improve performance under extreme contention by avoiding to reread all
fields.

A backport may be done to 2.4 if a problem is encountered, but last tests
on arm64 with LSE didn't show any issue so this can possibly stay as-is.

3 years agoMEDIUM: fd: rely more on fd_update_events() to detect changes
Willy Tarreau [Thu, 29 Jul 2021 14:57:19 +0000 (16:57 +0200)] 
MEDIUM: fd: rely more on fd_update_events() to detect changes

This function already performs a number of checks prior to calling the
IOCB, and detects the change of thread (FD migration). Half of the
controls are still in each poller, and these pollers also maintain
activity counters for various cases.

Note that the unreliable test on thread_mask was removed so that only
the one performed by fd_set_running() is now used, since this one is
reliable.

Let's centralize all that fd-specific logic into the function and make
it return a status among:

  FD_UPDT_DONE,        // update done, nothing else to be done
  FD_UPDT_DEAD,        // FD was already dead, ignore it
  FD_UPDT_CLOSED,      // FD was closed
  FD_UPDT_MIGRATED,    // FD was migrated, ignore it now

Some pollers already used to call it last and have nothing to do after
it, regardless of the result. epoll has to delete the FD in case a
migration is detected. Overall this removes more code than it adds.

3 years agoREORG: fd: uninline fd_update_events()
Willy Tarreau [Thu, 29 Jul 2021 14:53:46 +0000 (16:53 +0200)] 
REORG: fd: uninline fd_update_events()

This function has become a monster (80 lines and 2/3 of a kB), it doesn't
benefit from being static nor inline anymore, let's move it to fd.c.

3 years agoMINOR: poll/epoll: move detection of RDHUP support earlier
Willy Tarreau [Thu, 29 Jul 2021 14:19:24 +0000 (16:19 +0200)] 
MINOR: poll/epoll: move detection of RDHUP support earlier

Let's move the detection of support for RDHUP earlier and out of the
FD update chain, as it complicates its simplification.

3 years agoMINOR: fd: update flags only once in fd_update_events()
Willy Tarreau [Thu, 29 Jul 2021 06:04:00 +0000 (08:04 +0200)] 
MINOR: fd: update flags only once in fd_update_events()

Since 2.4 with commit f50906519 ("MEDIUM: fd: merge fdtab[].ev and state
for FD_EV_* and FD_POLL_* into state") we can merge all flag updates at
once in fd_update_events(). Previously this was performed in 1 to 3 steps,
setting the polling state, then setting READY_R if in/err/hup, and setting
READY_W if out/err. But since the commit above, all flags are stored
together in the same structure field that is being updated with the new
flags, thus we can simply update the flags altogether and avoid multiple
atomic operations. This even removes the need for atomic ops for FDs that
are not shared.

3 years agoDOC: internals: document the FD takeover process
Willy Tarreau [Fri, 30 Jul 2021 15:40:07 +0000 (17:40 +0200)] 
DOC: internals: document the FD takeover process

This explains the traps to avoid and the sequence that leads to
consistent use of an FD known by multiple threads at once. This
was co-authored with Olivier.

3 years agoBUG/MINOR: fd: protect fd state harder against a concurrent takeover
Willy Tarreau [Wed, 28 Jul 2021 15:20:53 +0000 (17:20 +0200)] 
BUG/MINOR: fd: protect fd state harder against a concurrent takeover

There's a theoretical race (that we failed to trigger) in function
fd_update_events(), which could strike on idle connections. The "locked"
variable will most often be 0 as the FD is bound to the current thread
only. Another thread could take it over once "locked" is set, change
the thread and running masks. Then the first thread updates the FD's
state non-atomically and possibly overwrites what the other thread was
preparing. It still looks like the FD's state will ultimately converge
though.

The solution against this is to set the running flag earlier so that a
takeover() attempt cannot succeed, or that the fd_set_running() attempt
fails, indicating that nothing needs to be done on this FD.

While this is sufficient for a simple fix to be backported, it leaves
the FD actively polled in the calling thread, this will trigger a second
wakeup which will notice the absence of tid_bit in the thread_mask,
getting rid of it.

A more elaborate solution would consist in calling fd_set_running()
directly from the pollers before calling fd_update_events(), getting
rid of the thread_mask test and letting the caller eliminate that FD
from its list if needed.

Interestingly, this code also proves to be suboptimal in that it sets
the FD state twice instead of calculating the new state at once and
always using a CAS to set it. This is a leftover of a simplification
that went into 2.4 and which should be explored in a future patch.

This may be backported as far as 2.2.

3 years agoBUG/MINOR: pollers: always program an update for migrated FDs
Willy Tarreau [Fri, 30 Jul 2021 12:18:49 +0000 (14:18 +0200)] 
BUG/MINOR: pollers: always program an update for migrated FDs

If an MT-aware poller reports that a file descriptor was migrated, it
must stop reporting it. The simplest way to do this is to program an
update if not done yet. This will automatically mark the FD for update
on next round. Otherwise there's a risk that some events are reported
a bit too often and cause extra CPU usage with these pollers. Note
that epoll is currently OK regarding this. Select does not need this
because it uses a single shared events table, so in case of migration
no FD change is expected.

This should be backported as far as 2.2.

3 years agoBUG/MINOR: poll: fix abnormally high skip_fd counter
Willy Tarreau [Fri, 30 Jul 2021 12:04:28 +0000 (14:04 +0200)] 
BUG/MINOR: poll: fix abnormally high skip_fd counter

The skip_fd counter that is incremented when a migrated FD is reported
was abnormally high in with poll. The reason is that it was accounted
for before preparing the polled events instead of being measured from
the reported events.

This mistake was done when the counters were introduced in 1.9 with
commit d80cb4ee1 ("MINOR: global: add some global activity counters to
help debugging"). It may be backported as far as 2.0.

3 years agoBUG/MINOR: select: fix excess number of dead/skip reported
Willy Tarreau [Fri, 30 Jul 2021 11:55:36 +0000 (13:55 +0200)] 
BUG/MINOR: select: fix excess number of dead/skip reported

In 1.8, commit ab62f5195 ("MINOR: polling: Use fd_update_events to update
events seen for a fd") updated the pollers to rely on fd_update_events(),
but the modification delayed the test of presence of the FD in the report,
resulting in owner/thread_mask and possibly event updates being performed
for each FD appearing in a block of 32 FDs around an active one. This
caused the request rate to be ~3 times lower with select() than poll()
under 6 threads.

This can be backported as far as 1.8.

3 years agoBUG/MEDIUM: pollers: clear the sleeping bit after waking up, not before
Willy Tarreau [Fri, 30 Jul 2021 08:57:09 +0000 (10:57 +0200)] 
BUG/MEDIUM: pollers: clear the sleeping bit after waking up, not before

A bug was introduced in 2.1-dev2 by commit 305d5ab46 ("MAJOR: fd: Get
rid of the fd cache."). Pollers "poll" and "evport" had the sleeping
bit accidentally removed before the syscall instead of after. This
results in them not being woken up by inter-thread wakeups, which is
particularly visible with the multi-queue accept() and with queues.

As a work-around, when these pollers are used, "nbthread 1" should
be used.

The fact that it has remained broken for 2 years is a great indication
that threads are definitely not enabled outside of epoll and kqueue,
hence why this patch is only tagged medium.

This must be backported as far as 2.2.

3 years agoBUG/MEDIUM: connection: close a rare race between idle conn close and takeover
Willy Tarreau [Wed, 28 Jul 2021 13:27:37 +0000 (15:27 +0200)] 
BUG/MEDIUM: connection: close a rare race between idle conn close and takeover

The takeover of idle conns between threads is particularly tricky, for
two reasons:
  - there's no way to atomically synchronize kernel-side polling with
    userspace activity, so late events will always be reported for some
    FDs just migrated ;

  - upon error, an FD may be immediately reassigned to whatever other
    thread since it's process-wide.

The current model uses the FD's thread_mask to figure if an FD still
ought to be reported or not, and a per-thread idle connection queue
from which eligible connections are atomically added/picked. I/Os
coming from the bottom for such a connection must remove it from the
list so that it's not elected. Same for timeout tasks and iocbs. And
these last ones check their context under the idle_conn lock to judge
if they're still allowed to run.

One rare case was omitted: the wake() callback. This one is rare, it
may serve to notify about finalized connect() calls that are not being
polled, as well as unhandled shutdowns and errors. This callback was
not protected till now because it wasn't seen as sensitive, but there
exists a particular case where it may be called without protectoin in
parallel to a takeover. This happens in the following sequence:

  - thread T1 wants to establish an outgoing connection
  - the connect() call returns EINPROGRESS
  - the poller adds it using epoll_ctl()
  - epoll_wait() reports it, connect() is done. The connection is
    not being marked as actively polled anymore but is still known
    from the poller.
  - the request is sent over that connection using send(), which
    queues to system buffers while data are being delivered
  - the scheduler switches to other tasks
  - the request is physically sent
  - the server responds
  - the stream is notified that send() succeeded, and makes progress,
    trying to recv() from that connection
  - the recv() succeeds, the response is delivered
  - the poller doesn't need to be touched (still no active polling)
  - the scheduler switches to other tasks
  - the server closes the connection
  - the poller on T1 is notified of the SHUTR and starts to call mux->wake()
  - another thread T2 takes over the connection
  - T2 continues to run inside wake() and releases the connection
  - T2 is just dereferencing it.
  - BAM.

The most logical solution here is to surround the call to wake() with an
atomic removal/insert of the connection from/into the idle conns lists.
This way, wake() is guaranteed to run alone. Any other poller reporting
the FD will not have its tid_bit in the thread_mask si will not bother
it. Another thread trying a takeover will not find this connection. A
task or tasklet being woken up late will either be on the same thread,
or be called on another one with a NULL context since it will be the
consequence of previous successful takeover, and will be ignored. Note
that the extra cost of a lock and tree access here have a low overhead
which is totally amortized given that these ones roughly happen 1-2
times per connection at best.

While it was possible to crash the process after 10-100k req using H2
and a hand-refined configuration achieving perfect synchronism between
a long (20+) chain of proxies and a short timeout (1ms), now with that
fix this never happens even after 10M requests.

Many thanks to Olivier for proposing this solution and explaining why
it works.

This should be backported as far as 2.2 (when inter-thread takeover was
introduced). The code in older versions will be found in conn_fd_handler().
A workaround consists in disabling inter-thread pool sharing using:

    tune.idle-pool.shared off

3 years agoREGTESTS: ssl: ssl_errors.vtc does not work with old openssl version
William Lallemand [Thu, 29 Jul 2021 14:00:24 +0000 (16:00 +0200)] 
REGTESTS: ssl: ssl_errors.vtc does not work with old openssl version

Disable the new ssl_errors.vtc reg-tests because in does not work
correctly on the CI since it requires a version of OpenSSL which is
compatible with TLSv1.3 and the ciphersuites keyword.

3 years agoREGTESTS: ssl: Add tests for the connection and SSL error fetches
Remi Tricot-Le Breton [Thu, 29 Jul 2021 07:45:54 +0000 (09:45 +0200)] 
REGTESTS: ssl: Add tests for the connection and SSL error fetches

This reg-test checks that the connection and SSL sample fetches related
to errors are functioning properly. It also tests the proper behaviour
of the default HTTPS log format and of the log-legacy-conn-error option
which enables or disables the output of a special error message in case
of connection failure (otherwise a line following the configured
log-format is output).

3 years agoMEDIUM: connection: Add option to disable legacy error log
Remi Tricot-Le Breton [Thu, 29 Jul 2021 07:45:53 +0000 (09:45 +0200)] 
MEDIUM: connection: Add option to disable legacy error log

In case of connection failure, a dedicated error message is output,
following the format described in section "Error log format" of the
documentation. These messages cannot be configured through a log-format
option.
This patch adds a new option, "dontloglegacyconnerr", that disables
those error logs when set, and "replaces" them by a regular log line
that follows the configured log-format (thanks to a call to sess_log in
session_kill_embryonic).
The new fc_conn_err sample fetch allows to add the legacy error log
information into a regular log format.
This new option is unset by default so the logging logic will remain the
same until this new option is used.

3 years agoMINOR: ssl: Define a default https log format
Remi Tricot-Le Breton [Thu, 29 Jul 2021 07:45:52 +0000 (09:45 +0200)] 
MINOR: ssl: Define a default https log format

This patch adds a new httpslog option and a new HTTP over SSL log-format
that expands the default HTTP format and adds SSL specific information.

3 years agoMINOR: ssl: Add new ssl_fc_hsk_err sample fetch
Remi Tricot-Le Breton [Thu, 29 Jul 2021 07:45:51 +0000 (09:45 +0200)] 
MINOR: ssl: Add new ssl_fc_hsk_err sample fetch

This new sample fetch along the ssl_fc_hsk_err_str fetch contain the
last SSL error of the error stack that occurred during the SSL
handshake (from the frontend's perspective). The errors happening during
the client's certificate verification will still be given by the
ssl_c_err and ssl_c_ca_err fetches. This new fetch will only hold errors
retrieved by the OpenSSL ERR_get_error function.

3 years agoMINOR: ssl: Enable error fetches in case of handshake error
Remi Tricot-Le Breton [Thu, 29 Jul 2021 07:45:50 +0000 (09:45 +0200)] 
MINOR: ssl: Enable error fetches in case of handshake error

The ssl_c_err, ssl_c_ca_err and ssl_c_ca_err_depth sample fetches values
were not recoverable when the connection failed because of the test
"conn->flags & CO_FL_WAIT_XPRT" (which required the connection to be
established). They could then not be used in a log-format since whenever
they would have sent a non-null value, the value fetching was disabled.
This patch ensures that all these values can be fetched in case of
connection failure.

3 years agoMINOR: connection: Add a connection error code sample fetch
Remi Tricot-Le Breton [Thu, 29 Jul 2021 07:45:49 +0000 (09:45 +0200)] 
MINOR: connection: Add a connection error code sample fetch

The fc_conn_err and fc_conn_err_str sample fetches give information
about the problem that made the connection fail. This information would
previously only have been given by the error log messages meaning that
thanks to these fetches, the error log can now be included in a custom
log format. The log strings were all found in the conn_err_code_str
function.

3 years agoBUG/MINOR: connection: Add missing error labels to conn_err_code_str
Remi Tricot-Le Breton [Thu, 29 Jul 2021 07:45:48 +0000 (09:45 +0200)] 
BUG/MINOR: connection: Add missing error labels to conn_err_code_str

The CO_ER_SSL_EARLY_FAILED and CO_ER_CIP_TIMEOUT connection error codes
were missing in the conn_err_code_str switch which converts the error
codes into string.

This patch can be backported on all stable branches.

3 years agoCLEANUP: mworker: PR_CAP already initialized with alloc_new_proxy()
William Lallemand [Thu, 29 Jul 2021 13:35:48 +0000 (15:35 +0200)] 
CLEANUP: mworker: PR_CAP already initialized with alloc_new_proxy()

Remove the PR_CAP initialization in mworker_cli_proxy_create() which is
already done in alloc_new_proxy().

3 years agoCLEANUP: mworker: use the proxy helper functions in mworker_cli_proxy_create()
William Lallemand [Thu, 29 Jul 2021 13:13:22 +0000 (15:13 +0200)] 
CLEANUP: mworker: use the proxy helper functions in mworker_cli_proxy_create()

Cleanup the mworker_cli_proxy_create() function by removing the
allocation and init of the proxy which is done manually, and replace it
by alloc_new_proxy(). Do the same with the free_proxy() function.

This patch also move the insertion at the end of the function.

3 years agoMINOR: stats: don't output internal proxies (PR_CAP_INT)
William Lallemand [Wed, 28 Jul 2021 15:45:18 +0000 (17:45 +0200)] 
MINOR: stats: don't output internal proxies (PR_CAP_INT)

Disable the output of the statistics of internal proxies (PR_CAP_INT),
wo we don't rely only on the px->uuid > 0. This will allow to hide more
cleanly the internal proxies in the stats.

3 years agoMINOR: mworker: the mworker CLI proxy is internal
William Lallemand [Wed, 28 Jul 2021 15:40:56 +0000 (17:40 +0200)] 
MINOR: mworker: the mworker CLI proxy is internal

Sets the mworker CLI proxy as a internal one (PR_CAP_INT) so we could
exlude it from stats and other tests.

3 years agoMINOR: proxy: rename PR_CAP_LUA to PR_CAP_INT
William Lallemand [Wed, 28 Jul 2021 13:48:16 +0000 (15:48 +0200)] 
MINOR: proxy: rename PR_CAP_LUA to PR_CAP_INT

This patch renames the proxy capability "LUA" to "INT" so it could be
used for any internal proxy.

Every proxy that are not user defined should use this flag.

3 years agoBUG/MEDIUM: mux-h2: Handle remaining read0 cases on partial frames
Christopher Faulet [Mon, 26 Jul 2021 10:06:53 +0000 (12:06 +0200)] 
BUG/MEDIUM: mux-h2: Handle remaining read0 cases on partial frames

This part was fixed several times since commit aade4edc1 ("BUG/MEDIUM:
mux-h2: Don't handle pending read0 too early on streams") and there are
still some cases where a read0 event may be ignored because a partial frame
inhibits the event.

Here, we must take care to set H2_CF_END_REACHED flag if a read0 was
received while a partial frame header is received or if the padding length
is missing.

To ease partial frame detection, H2_CF_DEM_SHORT_READ flag is introduced. It
is systematically removed when some data are received and is set when a
partial frame is found or when dbuf buffer is empty. At the end of the
demux, if the connection must be closed ASAP or if data are missing to move
forward, we may acknowledge the pending read0 event, if any. For now,
H2_CF_DEM_SHORT_READ is not part of H2_CF_DEM_BLOCK_ANY mask.

This patch should fix the issue #1328. It must be backported as far as 2.0.

3 years agoBUG/MINOR: mux-h1: Be sure to swap H1C to splice mode when rcv_pipe() is called
Christopher Faulet [Mon, 26 Jul 2021 08:49:39 +0000 (10:49 +0200)] 
BUG/MINOR: mux-h1: Be sure to swap H1C to splice mode when rcv_pipe() is called

The splicing does not work anymore because the H1 connection is not swap to
splice mode when rcv_pipe() callback function is called. It is important to
set H1C_F_WANT_SPLICE flag to inhibit data receipt via the buffer
API. Otherwise, because there are always data in the buffer, it is not
possible to use the kernel splicing.

This bug was introduced by the commit 2b861bf72 ("MINOR: mux-h1: clean up
conditions to enabled and disabled splicing").

The patch must be backported to 2.4.

3 years agoBUG/MINOR: mux-h2: Obey dontlognull option during the preface
Christopher Faulet [Mon, 26 Jul 2021 08:18:35 +0000 (10:18 +0200)] 
BUG/MINOR: mux-h2: Obey dontlognull option during the preface

If a connection is closed during the preface while no data are received, if
the dontlognull option is set, no log message must be emitted. However, this
will still be handled as a protocol error. Only the log is omitted.

This patch should fix the issue #1336 for H2 sessions. It must be backported
to 2.4 and 2.3 at least, and probably as far as 2.0.