Tim Duesterhus [Tue, 15 Mar 2022 12:11:06 +0000 (13:11 +0100)]
CLEANUP: Reapply ist.cocci with `--include-headers-for-types --recursive-includes`
Previous uses of `ist.cocci` did not add `--include-headers-for-types` and
`--recursive-includes` preventing Coccinelle seeing `struct ist` members of
other structs.
Reapply the patch with proper flags to further clean up the use of the ist API.
BUG/MINOR: http-rules: Don't free new rule on allocation failure
If allocation of a new HTTP rule fails, we must not release it calling
free_act_rule(). The regression was introduced by the commit dd7e6c6dc
("BUG/MINOR: http-rules: completely free incorrect TCP rules on error").
This patch must only be backported if the commit above is backported. It should
fix the issues #1627, #1628 and #1629.
BUG/MINOR: rules: Initialize the list element when allocating a new rule
dd7e6c6dc ("BUG/MINOR: http-rules: completely free incorrect TCP rules on
error") and 388c0f2a6 ("BUG/MINOR: tcp-rules: completely free incorrect TCP
rules on error") introduced a regression because the list element of a new
rule is not intialized. Thus HAProxy crashes when an incorrect rule is
released.
This patch must be backported if above commits are backported. Note that
new_act_rule() only exists since the 2.5. It relies on the commit d535f807b
("MINOR: rules: add a new function new_act_rule() to allocate act_rules").
Willy Tarreau [Fri, 18 Mar 2022 14:57:34 +0000 (15:57 +0100)]
BUG/MEDIUM: mux-h2: make use of http-request and keep-alive timeouts
Christian Ruppert reported an issue explaining that it's not possible to
forcefully close H2 connections which do not receive requests anymore if
they continue to send control traffic (window updates, ping etc). This
will indeed refresh the timeout. In H1 we don't have this problem because
any single byte is part of the stream, so the control frames in H2 would
be equivalent to TCP acks in H1, that would not contribute to the timeout
being refreshed.
What misses from H2 is the use of http-request and keep-alive timeouts.
These were not implemented because initially it was hard to see how they
could map to H2. But if we consider the real use of the keep-alive timeout,
that is, how long do we keep a connection alive with no request, then it's
pretty obvious that it does apply to H2 as well. Similarly, http-request
may definitely be honored as soon as a HEADERS frame starts to appear
while there is no stream. This will also allow to deal with too long
CONTINUATION frames.
This patch moves the timeout update to a new function, h2c_update_timeout(),
which is in charge of this. It also adds an "idle_start" timestamp in the
connection, which is set when nb_cs reaches zero or when a headers frame
start to arrive, so that it cannot be delayed too long.
This patch should be backported to recent stable releases after some
observation time. It depends on previous patch "MEDIUM: mux-h2: slightly
relax timeout management rules".
The H2 timeout rules were arranged to cover complex situations In 2.1
with commit c2ea47fb1 ("BUG/MEDIUM: mux-h2: do not enforce timeout on
long connections").
It turns out that such rules while complex, do not perfectly cover all
use cases. The real intent is to say that as long as there are attached
streams, the connection must not timeout. Then once all these streams
have quit (possibly for timeout reasons) then the mux should take over
the management of timeouts.
We do have this nb_cs field which indicates the number of attached
streams, and it's updated even when leaving orphaned streams. So
checking it alone is sufficient to know whether it's the mux or the
streams that are in charge of the timeouts.
In its current state, this doesn't cause visible effects except that
it makes it impossible to implement more subtle parsing timeouts.
This would need to be backported as far as 2.0 along with the next
commit that will depend on it.
Willy Tarreau [Fri, 18 Mar 2022 16:37:20 +0000 (17:37 +0100)]
BUG/MEDIUM: trace: avoid race condition when retrieving session from conn->owner
There's a rare race condition possible when trying to retrieve session from
a back connection's owner, that was fixed in 2.4 and described in commit 3aab17bd5 ("BUG/MAJOR: connection: reset conn->owner when detaching from
session list").
It also affects the trace code which does the same, so the same fix is
needed, i.e. check from conn->session_list that the connection is still
enlisted. It's visible when sending a few tens to hundreds of parallel
requests to an h2 backend and enabling traces in parallel.
This should be backported as far as 2.2 which is the oldest version
supporting traces.
Willy Tarreau [Thu, 17 Mar 2022 15:19:09 +0000 (16:19 +0100)]
BUG/MEDIUM: stream-int: do not rely on the connection error once established
Historically the stream-interface code used to check for connection
errors by itself. Later this was partially deferred to muxes, but
only once the mux is installed or the connection is at least in the
established state. But probably as a safety practice the connection
error tests remained.
The problem is that they are causing trouble on when a response received
from a mux is mixed with an error report. The typical case is an upload
that is interrupted by the server sending an error or redirect without
draining all data, causing an RST to be queued just after the data. In
this case the mux has the data, the CO_FL_ERROR flag is present on the
connection, and unfortunately the stream-interface refuses to retrieve
the data due to this flag, and return an error to the client.
It's about time to only rely on CS_FL_ERROR which is set by the mux, but
the stream-interface is still responsible for the connection during its
setup. However everywhere the CO_FL_ERROR is checked, CS_FL_ERROR is
also checked.
This commit addresses this by:
- adding a new function si_is_conn_error() that checks the SI state
and only reports the status of CO_FL_ERROR for states before
SI_ST_EST.
- eliminating all checks for CO_FL_ERORR in places where CS_FL_ERROR
is already checked and either the presence of a mux was already
validated or the stream-int's state was already checked as being
SI_ST_EST or higher.
CO_FL_ERROR tests on the send() direction are also inappropriate as they
may cause the loss of pending data. Now this doesn't happen anymore and
such events are only converted to CS_FL_ERROR by the mux once notified of
the problem. As such, this must not cause the loss of any error event.
Now an early error reported on a backend mux doesn't prevent the queued
response from being read and forwarded to the client (the list of syscalls
below was trimmed and epoll_ctl is not represented):
Above the server is an haproxy configured with the following:
listen blah
bind :8002
mode http
timeout connect 5s
timeout client 5s
timeout server 5s
option httpclose
option nolinger
http-request return status 200 hdr connection close
And the client takes care of sending requests and data in two distinct
parts:
while :; do
./dev/tcploop/tcploop 8001 C T S:"POST / HTTP/1.1\r\nConnection: close\r\nTransfer-encoding: chunked\r\n\r\n" P1 S:"A\n0123456789\r\n0\r\n\r\n" P R F;
done
With this, a small percentage of the requests will reproduce the behavior
above. Note that this fix requires the following patch to be applied for
the test above to work:
BUG/MEDIUM: mux-h1: only turn CO_FL_ERROR to CS_FL_ERROR with empty ibuf
This should be backported with after a few weeks of observation, and
likely one version at a time. During the backports, the patch might
need to be adjusted at each check of CO_FL_ERORR to follow the
principles explained above.
Willy Tarreau [Thu, 17 Mar 2022 16:10:36 +0000 (17:10 +0100)]
BUG/MEDIUM: mux-h1: only turn CO_FL_ERROR to CS_FL_ERROR with empty ibuf
A connection-level error must not be turned to a stream-level error if there
are still pending data for that stream, otherwise it can cause the truncation
of the last pending data.
This must be backported to affected releases, at least as far as 2.4,
maybe further.
BUG/MINOR: httpclient: process the response when received before the end of the request
A server could reply a response with a shut before the end of the htx
transfer, in this case the httpclient would leave before computing the
received response.
This patch fixes the issue by calling the "process_data" label instead of
the "more" label which don't do the si_shut.
BUG/MINOR: httpclient: only check co_data() instead of HTTP_MSG_DATA
Checking msg >= HTTP_MSG_DATA was useful to check if we received all the
data. However it does not work correctly in case of errors because we
don't reach this state, preventing to catch the error in the httpclient.
The consequence of this problem is that we don't get the status code of
the error response upon an error.
Willy Tarreau [Thu, 17 Mar 2022 19:29:06 +0000 (20:29 +0100)]
BUG/MINOR: http-rules: completely free incorrect TCP rules on error
When a http-request or http-response rule fails to parse, we currently
free only the rule without its contents, which makes ASAN complain.
Now that we have a new function for this, let's completely free the
rule. This relies on this commit:
MINOR: actions: add new function free_act_rule() to free a single rule
It's probably not needed to backport this since we're on the exit path
anyway.
Willy Tarreau [Thu, 17 Mar 2022 19:26:54 +0000 (20:26 +0100)]
BUG/MINOR: tcp-rules: completely free incorrect TCP rules on error
When a tcp-request or tcp-response rule fails to parse, we currently
free only the rule without its contents, which makes ASAN complain.
Now that we have a new function for this, let's completely free the
rule. Reg-tests are now completely OK with ASAN. This relies on this
commit:
MINOR: actions: add new function free_act_rule() to free a single rule
It's probably not needed to backport this since we're on the exit path
anyway.
Willy Tarreau [Thu, 17 Mar 2022 19:23:43 +0000 (20:23 +0100)]
MINOR: actions: add new function free_act_rule() to free a single rule
There was free_act_rules() that frees all rules from a head but nothing
to free a single rule. Currently some rulesets partially free their own
rules on parsing error, and we're seeing some regtests emit errors under
ASAN because of this.
Let's first extract the code to free a rule into its own function so
that it becomes possible to use it on a single rule.
Willy Tarreau [Thu, 17 Mar 2022 18:47:33 +0000 (19:47 +0100)]
BUG/MINOR: logs: fix logsrv leaks on clean exit
Log servers are a real mess because:
- entries are duplicated using memcpy() without their strings being
reallocated, which results in these ones not being freeable every
time.
- a new field, ring_name, was added in 2.2 by commit 99c453df9
("MEDIUM: ring: new section ring to declare custom ring buffers.")
but it's never initialized during copies, causing the same issue
- no attempt is made at freeing all that.
Of course, running "haproxy -c" under ASAN quickly notices that and
dumps a core.
This patch adds the missing strdup() and initialization where required,
adds a new free_logsrv() function to cleanly free() such a structure,
calls it from the proxy when iterating over logsrvs instead of silently
leaking their file names and ring names, and adds the same logsrv loop
to the proxy_free_defaults() function so that we don't leak defaults
sections on exit.
It looks a bit entangled, but it comes as a whole because all this stuff
is inter-dependent and was missing.
It's probably preferable not to backport this in the foreseable future
as it may reveal other jokes if some obscure parts continue to memcpy()
the logsrv struct.
BUG/MINOR: server/ssl: free the SNI sample expression
ASAN complains about the SNI expression not being free upon an haproxy
-c. Indeed the httpclient is now initialized with a sni expression and
this one is never free in the server release code.
Must be backported in 2.5 and could be backported in every stable
versions.
Fix the build of the httpclient without SSL, the problem was introduced
with previous patch 71e3158 ("BUG/MINOR: httpclient: send the SNI using
the host header")
Willy Tarreau [Wed, 16 Mar 2022 13:49:33 +0000 (14:49 +0100)]
DEV: udp: switch parser to getopt() instead of positional arguments
In order to ease addition of new types of perturbations to udp-perturb,
let's first switch to getopt() and get rid of the positional arguments.
The random seed was already a conditional option of the rate which was
a conditional option as well. We add -r and -s for the rate and the
seed, and new options will follow.
BUG/MEDIUM: cli/debug: Properly get the stream-int in all debug I/O handlers
The appctx owner is not a stream-interface anymore. It is now a conn-stream.
In the cli I/O handler for the command "debug dev fd", we still handle it as
a stream-interface. It is now fixed.
BUG/MEDIUM: applet: Don't call .release callback function twice
Since the CS/SI refactoring, the .release callback function may be called
twice. The first call when a shutdown for read or for write is performed.
The second one when the applet is detached from its conn-stream. The second
call must be guarded, just like the first one, to only be performed is the
stream-interface is not the in disconnected (SI_ST_DIS) or closed
(SI_ST_CLO) state.
To simplify the fix, we now always rely on si_applet_release() function.
BUG/MINOR: httpclient/lua: stuck when closing without data
The httpclient lua code is lacking the end callback, which means it
won't be able to wake up the lua code after a longjmp if the connection
was closed without any data.
BUG/MAJOR: quic: Possible crash with full congestion control window
This commit reverts this one:
"d5066dd9d BUG/MEDIUM: quic: qc_prep_app_pkts() retries on qc_build_pkt() failures"
After having filled the congestion control window, qc_build_pkt() always fails.
Then depending on the relative position of the writer and reader indexes for the
TX buffer, this could lead this function to try to reuse the buffer even if not full.
In such case, we do not always mark the end of the data in this TX buffer. This
is something the reader cannot understand: it reads a false datagram length,
then a wrong packet address from the TX buffer, leading to an invalid pointer
dereferencing.
BUG/MEDIUM: quic: Blocked STREAM when retransmitted
STREAM frames which are not acknowledged in order are inserted in ->tx.acked_frms
tree ordered by the STREAM frame offset values. Then, they are consumed in order
by qcs_try_to_consume(). But, when we retransmit frames, we possibly have to
insert the same STREAM frame node (with the same offset) in this tree.
The problem is when they have different lengths. Unfortunately the restransmitted
frames are not inserted because of the tree nature (EB_ROOT_UNIQUE). If the STREAM
frame which has been successfully inserted has a smaller length than the
retransmitted ones, when it is consumed they are tailing bytes in the STREAM
(retransmitted ones) which indefinitively remains in the STREAM TX buffer
which will never properly be consumed, leading to a blocking state.
At this time this may happen because we sometimes build STREAM frames
with null lengths. But this is another issue.
The solution is to use an EB_ROOT tree to support the insertion of STREAM frames
with the same offset but with different lengths. As qcs_try_to_consume() support
the STREAM frames retransmission this modification should not have any impact.
In the same way than for be2hex.vtc, a "Connection: close" header is added
to all responses to avoid any connection reuse. This should avoid any "HTTP
header incomplete" errors.
BUG/MEDIUM: httpclient: must manipulate head, not first
The httpclient mistakenly use the htx_get_first{_blk}() functions instead
of the htx_get_head{_blk}() functions. Which could stop the httpclient
because it will be without the start line, waiting for data that won't never
come.
BUG/MINOR: httpclient: remove the UNUSED block when parsing headers
Remove the UNUSED blocks when iterating on headers, we should not stop
when encountering one. We should only stop iterating once we found the
EOH block. It doesn't provoke a problem, since we don't manipulates
the headers before treating them, but it could evolve in the future.
BUG/MINOR: httpclient: consume partly the blocks when necessary
Consume partly the blocks in the httpclient I/O handler when there is
not enough room in the destination buffer for the whole block or when
the block is not contained entirely in the channel's output.
It prevents the I/O handler to be stuck in cases when we need to modify
the buffer with a filter for exemple.
BUG/MEDIUM: httpclient: don't consume data before it was analyzed
In httpclient_applet_io_handler(), on the response path, we don't check
if the data are in the output part of the channel, and could consume
them before they were analyzed.
To fix this issue, this patch checks for the stline and the headers if
the msg_state is >= HTTP_MSG_DATA which means the stline and headers
were analyzed. For the data part, it checks if each htx blocks is in the
output before copying it.
Willy Tarreau [Fri, 11 Mar 2022 17:09:24 +0000 (18:09 +0100)]
[RELEASE] Released version 2.6-dev3
Released version 2.6-dev3 with the following main changes :
- DEBUG: rename WARN_ON_ONCE() to CHECK_IF()
- DEBUG: improve BUG_ON output message accuracy
- DEBUG: implement 4 levels of choices between warn and crash.
- DEBUG: add two new macros to enable debugging in hot paths
- DEBUG: buf: replace some sensitive BUG_ON() with BUG_ON_HOT()
- DEBUG: buf: add BUG_ON_HOT() to most buffer management functions
- MINOR: channel: don't use co_set_data() to decrement output
- DEBUG: channel: add consistency checks using BUG_ON_HOT() in some key functions
- MINOR: conn-stream: Improve API to have safe/unsafe accessors
- MEDIUM: tree-wide: Use unsafe conn-stream API when it is relevant
- CLEANUP: stream-int: Make si_cs_send() function static
- REORG: stream-int: Uninline si_sync_recv() and make si_cs_recv() private
- BUG/MEDIUM: mux-fcgi: Don't rely on SI src/dst addresses for FCGI health-checks
- BUG/MEDIUM: htx: Fix a possible null derefs in htx_xfer_blks()
- REGTESTS: fix the race conditions in normalize_uri.vtc
- DEBUG: stream-int: Fix BUG_ON used to test appctx in si_applet_ops callbacks
- BUILD: debug: fix build warning on older compilers around DEBUG_STRICT_ACTION
- CLEANUP: connection: Indicate unreachability to the compiler in conn_recv_proxy
- MINOR: connection: Transform safety check in PROXYv2 parsing into BUG_ON()
- DOC: install: it's DEBUG_CFLAGS, not DEBUG, which is set to -g
- DOC: install: describe the DEP variable
- DOC: install: describe how to choose options used in the DEBUG variable
- MINOR: queue: Replace if() + abort() with BUG_ON()
- CLEANUP: adjust indentation in bidir STREAM handling function
- MINOR: quic: simplify copy of STREAM frames to RX buffer
- MINOR: quic: handle partially received buffered stream frame
- MINOR: mux-quic: define flag for last received frame
- BUG/MINOR: quic: support FIN on Rx-buffered STREAM frames
- MEDIUM: quic: rearchitecture Rx path for bidirectional STREAM frames
- REGTESTS: fix the race conditions in secure_memcmp.vtc
- CLEANUP: stream: Remove useless tests on conn-stream in stream_dump()
- BUILD: ssl: another build warning on LIBRESSL_VERSION_NUMBER
- MINOR: quic: Ensure PTO timer is not set in the past
- MINOR: quic: Post handshake I/O callback switching
- MINOR: quic: Drop the packets of discarded packet number spaces
- CLEANUP: quic: Useless tests in qc_try_rm_hp()
- CLEANUP: quic: Indentation fix in qc_prep_pkts()
- MINOR: quic: Assemble QUIC TLS flags at the same level
- BUILD: conn_stream: avoid null-deref warnings on gcc 6
- BUILD: connection: do not declare register_mux_proto() inline
- BUILD: http_rules: do not declare http_*_keywords_registre() inline
- BUILD: trace: do not declare trace_registre_source() inline
- BUILD: tcpcheck: do not declare tcp_check_keywords_register() inline
- DEBUG: reduce the footprint of BUG_ON() calls
- BUG/MEDIUM: httpclient/lua: infinite appctx loop with POST
- BUG/MINOR: pool: always align pool_heads to 64 bytes
- DEV: udp: add a tiny UDP proxy for testing
- DEV: udp: implement pseudo-random reordering/loss
- DEV: udp: add an optional argument to set the prng seed
- BUG/MINOR: quic: fix segfault on CC if mux uninitialized
- BUG/MEDIUM: pools: fix ha_free() on area in the process of being freed
- CLEANUP: tree-wide: remove a few rare non-ASCII chars
- CI: coverity: simplify debugging options
- CLEANUP: quic: complete ABORT_NOW with a TODO comment
- MINOR: quic: qc_prep_app_pkts() implementation
- MINOR: quic: Send short packet from a frame list
- MINOR: quic: Make qc_build_frms() build ack-eliciting frames from a list
- MINOR: quic: Export qc_send_app_pkts()
- MINOR: mux-quic: refactor transport parameters init
- MINOR: mux-quic: complete functions to detect stream type
- MINOR: mux-quic: define new unions for flow-control fields
- MEDIUM: mux-quic: use direct send transport API for STREAMs
- MINOR: mux-quic: retry send opportunistically for remaining frames
- MEDIUM: mux-quic: implement MAX_STREAMS emission for bidir streams
- BUILD: fix kFreeBSD build.
- MINOR: quic: Retry on qc_build_pkt() failures
- BUG/MINOR: quic: Missing recovery start timer reset
- CLEANUP: quic: Remove QUIC path manipulations out of the congestion controller
- MINOR: quic: Add a "slow start" callback to congestion controller
- MINOR: quic: Persistent congestion detection outside of controllers
- CLEANUP: quic: Remove useless definitions from quic_cc_event struct
- BUG/MINOR: quic: Confusion betwen "in_flight" and "prep_in_flight" in quic_path_prep_data()
- MINOR: quic: More precise window update calculation
- CLEANUP: quic: Remove window redundant variable from NewReno algorithm state struct
- MINOR: quic: Add quic_max_int_by_size() function
- BUG/MAJOR: quic: Wrong quic_max_available_room() returned value
- MINOR: pools: add a new global option "no-memory-trimming"
- BUG/MINOR: add missing modes in proxy_mode_str()
- BUG/MINOR: cli: shows correct mode in "show sess"
- BUG/MEDIUM: quic: do not drop packet on duplicate stream/decoding error
- MINOR: stats: Add dark mode support for socket rows
- BUILD: fix recent build breakage of freebsd caused by kFreeBSD build fix
- BUG/MINOR: httpclient: Set conn-stream/channel EOI flags at the end of request
- BUG/MINOR: hlua: Set conn-stream/channel EOI flags at the end of request
- BUG/MINOR: stats: Set conn-stream/channel EOI flags at the end of request
- BUG/MINOR: cache: Set conn-stream/channel EOI flags at the end of request
- BUG/MINOR: promex: Set conn-stream/channel EOI flags at the end of request
- BUG/MEDIUM: stream: Use the front analyzers for new listener-less streams
- DEBUG: cache: Update underlying buffer when loading HTX message in cache applet
- BUG/MEDIUM: mcli: Properly handle errors and timeouts during reponse processing
- DEBUG: stream: Add the missing descriptions for stream trace events
- DEBUG: stream: Fix stream trace message to print response buffer state
- MINOR: proxy: Store monitor_uri as a `struct ist`
- MINOR: proxy: Store fwdfor_hdr_name as a `struct ist`
- MINOR: proxy: Store orgto_hdr_name as a `struct ist`
- MEDIUM: proxy: Store server_id_hdr_name as a `struct ist`
- CLEANUP: fcgi: Replace memcpy() on ist by istcat()
- CLEANUP: fcgi: Use `istadv()` in `fcgi_strm_send_params`
- BUG/MAJOR: mux-pt: Always destroy the backend connection on detach
- DOC: sample fetch methods: move distcc_* to the right locations
- MINOR: rules: record the last http/tcp rule that gave a final verdict
- MINOR: stream: add "last_rule_file" and "last_rule_line" samples
- BUG/MINOR: session: fix theoretical risk of memleak in session_accept_fd()
- MINOR: quic: Add max_idle_timeout advertisement handling
- MEDIUM: quic: Remove the QUIC connection reference counter
- BUG/MINOR: quic: ACK_REQUIRED and ACK_RECEIVED flag collision
- BUG/MINOR: quic: Missing check when setting the anti-amplification limit as reached
- MINOR: quic: Add a function to compute the current PTO
- MEDIUM: quic: Implement the idle timeout feature
- BUG/MEDIUM: quic: qc_prep_app_pkts() retries on qc_build_pkt() failures
- CLEANUP: quic: Comments fix for qc_prep_(app)pkts() functions
- MINOR: mux-quic: prevent push frame for unidir streams
- MINOR: mux-quic: improve opportunistic retry sending for STREAM frames
- MINOR: quic: implement sending confirmation
- MEDIUM: mux-quic: improve bidir STREAM frames sending
- MEDIUM: check: do not auto configure SSL/PROXY for dynamic servers
- REGTESTS: server: test SSL/PROXY with checks for dynamic servers
- MEDIUM: server: remove experimental-mode for dynamic servers
- BUG/MINOR: buffer: fix debugging condition in b_peek_varint()
Willy Tarreau [Fri, 11 Mar 2022 15:55:49 +0000 (16:55 +0100)]
BUG/MINOR: buffer: fix debugging condition in b_peek_varint()
The BUG_ON_HOT() test condition added to b_peek_varint() by commit 8873b85bd ("DEBUG: buf: add BUG_ON_HOT() to most buffer management
functions") was wrong as <data> in this function is not b->data,
so that was triggering during live dumps of H2 traces on the CLI
when built with -DDEBUG_STRICT=2. No backport is needed.
MEDIUM: server: remove experimental-mode for dynamic servers
Dynamic servers feature is now judged to be stable enough. Remove the
experimental-mode requirement for "add/del server" commands. This should
facilitate dynamic servers adoption.
MEDIUM: check: do not auto configure SSL/PROXY for dynamic servers
For server checks, SSL and PROXY is automatically inherited from the
server settings if no specific check port is specified. Change this
behavior for dynamic servers : explicit "check-ssl"/"check-send-proxy"
are required for them.
Without this change, it is impossible to add a dynamic server with
SSL/PROXY settings and checks without, if the check port is not
explicit. This is because "no-check-ssl"/"no-check-send-proxy" keywords
are not available for dynamic servers.
This change respects the principle that dynamic servers on the CLI
should not reuse the same shortcuts used during the config file parsing.
Mostly because we expect this feature to be manipulated by automated
tools, contrary to the config file which should aim to be the shortest
possible for human readability.
Update the documentation of the "check" keyword to reflect this change.
The current implementation of STREAM frames emission has some
limitation. Most notably when we cannot sent all frames in a single
qc_send run.
In this case, frames are left in front of the MUX list. It will be
re-send individually before other frames, possibly another frame from
the same STREAM with new data. An opportunity to merge the frames is
lost here.
This method is now improved. If a frame cannot be send entirely, it is
discarded. On the next qc_send run, we retry to send to this position. A
new field qcs.sent_offset is used to remember this. A new frame list is
used for each qc_send.
The impact of this change is not precisely known. The most notable point
is that it is a more logical method of emission. It might also improve
performance as we do not keep old STREAM frames which might delay other
streams.
Amaury Denoyelle [Thu, 10 Mar 2022 15:42:23 +0000 (16:42 +0100)]
MINOR: mux-quic: improve opportunistic retry sending for STREAM frames
For the moment, the transport layer function qc_send_app_pkts lacks
features. Most notably, it only send up to a single Tx buffer and won't
retry even if there is frames left and its Tx buffer is now empty.
To overcome this limitation, the MUX implements an opportunistic retry
sending mechanism. qc_send_app_pkts is repeatedly called until the
transport layer is blocked on an external condition (such as congestion
control or a sendto syscall error).
The blocking was detected by inspecting the frame list before and after
qc_send_app_pkts. If no frame has been poped by the function, we
considered the transport layer to be blocked and we stop to send. The
MUX is subscribed on the lower layer to send the frames left.
However, in case of STREAM frames, qc_send_app_pkts might use only a
portion of the data and update the frame offset. So, for STREAM frames,
a new mechanism is implemented : if the offset field of the first frame
has not been incremented, it means the transport layer is blocked.
This should improve transfers execution. Before this change, there is a
possibility of interrupted transfer if the mux has not sent everything
possible and is waiting on a transport signaling which will never
happen.
In the future, qc_send_app_pkts should be extended to retry sending by
itself. All this code burden will be removed from the MUX.
Amaury Denoyelle [Thu, 10 Mar 2022 15:46:18 +0000 (16:46 +0100)]
MINOR: mux-quic: prevent push frame for unidir streams
For the moment, unidirectional streams handling is not identical to
bidirectional ones in MUX/H3 layer, both in Rx and Tx path. As a safety,
skip over uni streams in qc_send.
In fact, this change has no impact because qcs.tx.buf is emptied before
we start using qcs_push_frame, which prevents the call to
qcs_push_frame. However, this condition will soon change to improve
bidir streams emission, so an explicit check on stream type must be
done.
It is planified to unify uni and bidir streams handling in a future
stage. When implemented, the check will be removed.
The aim of the idle timeout is to silently closed the connection after a period
of inactivity depending on the "max_idle_timeout" transport parameters advertised
by the endpoints. We add a new task to implement this timer. Its expiry is
updated each time we received an ack-eliciting packet, and each time we send
an ack-eliciting packet if no other such packet was sent since we received
the last ack-eliciting packet. Such conditions may be implemented thanks
to QUIC_FL_CONN_IDLE_TIMER_RESTARTED_AFTER_READ new flag.
BUG/MINOR: quic: ACK_REQUIRED and ACK_RECEIVED flag collision
This packet number space flags were defined with the same value because
defined at different places in the file. Assemble them at the same location
with different values.
This bug could unvalidate the peer address after it was validated
during the handshake leading to the anti-amplication limit to be
enabled again after having been disabled. The situation could not
be unblocked (deadlock).
MEDIUM: quic: Remove the QUIC connection reference counter
There is no need to use such a reference counter anymore since the QUIC
connections are always handled by the same thread.
quic_conn_drop() is removed. Its code is merged into quic_conn_release().
When we store the remote transport parameters, we compute the maximum idle
timeout for the connection which is the minimum of the two advertised
max_idle_timeout transport parameter values if both have non-null values, or the
maximum if one of the value is set and non-null.
Willy Tarreau [Fri, 11 Mar 2022 06:25:11 +0000 (07:25 +0100)]
BUG/MINOR: session: fix theoretical risk of memleak in session_accept_fd()
Andrew Suffield reported in issue #1596 that we've had a bug in
session_accept_fd() since 2.4 with commit 1b3c931bf ("MEDIUM:
connections: Introduce a new XPRT method, start().") where an error
label is wrong and may cause the leak of the freshly allocated session
in case conn_xprt_start() returns < 0.
The code was checked there and the only two transport layers available
at this point are raw_sock and ssl_sock. The former doesn't provide a
->start() method hence conn_xprt_start() will always return zero. The
second does provide such a function, but it may only return <0 if the
underlying transport (raw_sock) has such a method and fails, which is
thus not the case.
So fortunately it is not possible to trigger this leak.
The patch above also touched the accept code in quic_sock() which was
mostly a plain copy of the session code, but there the move didn't
have this impact, and since then it was simplified and the next change
moved it to its final destination with the proper error label.
This should be backported as far as 2.4 as a long-term safety measure
(e.g. if in the future we have a reason for making conn_xprt_start()
to start failing), but will not have any positive nor negative effect
in the short term.
Willy Tarreau [Wed, 9 Mar 2022 16:33:05 +0000 (17:33 +0100)]
MINOR: stream: add "last_rule_file" and "last_rule_line" samples
These two sample fetch methods report respectively the file name and the
line number where was located the last rule that was final. This is aimed
at being used on log-format lines to help admins figure what rule in the
configuration gave a final verdict, and help understand the condition
that led to the action.
For example, it's now possible to log the last matched rule by adding
this to the log-format:
... lr=%[last_rule_file]:%[last_rule_line]
A regtest is provided to test various combinations of final rules, some
even on top of each other from different rulesets.
Willy Tarreau [Wed, 9 Mar 2022 16:23:10 +0000 (17:23 +0100)]
MINOR: rules: record the last http/tcp rule that gave a final verdict
When a tcp-{request,response} content or http-request/http-response
rule delivers a final verdict (deny, accept, redirect etc), the last
evaluated one will now be recorded in the stream. The purpose is to
permit to log the last one that performed a final action. For now
the log is not produced.
Willy Tarreau [Thu, 10 Mar 2022 09:39:58 +0000 (10:39 +0100)]
DOC: sample fetch methods: move distcc_* to the right locations
The distcc* sample fetch methods were surprisingly located within the
"internal state" section, while they in fact depend on L6 contents.
This can be backported to all versions where they appear.
BUG/MAJOR: mux-pt: Always destroy the backend connection on detach
In TCP, when a conn-stream is detached from a backend connection, the
connection must be always closed. It was only performed if an error or a
shutdown occurred or if there was no connection owner. But it is a problem,
because, since the 2.3, backend connections are always owned by a
session. This way it is possible to have idle connections attached to a
session instead of a server. But there is no idle connections in TCP. In
addition, when a session owns a connection it is responsible to close it
when it is released. But it only works for idle connections. And it only
works if the session is released.
Thus there is the place for bugs here. And indeed, a connection leak may
occur if a connection retry is performed because of a timeout. In this case,
the underlying connection is still alive and is waiting to be fully
established. Thus, when the conn-stream is detached from the connection, the
connection is not closed. Because the PT multiplexer is quite simple, there
is no timeout at this stage. We depend on the kenerl to be notified and
finally close the connection. With an unreachable server, orphan backend
connections may be accumulated for a while. It may be perceived as a leak.
Because there is no reason to keep such backend connections, we just close
it now. Frontend connections are still closed by the session or when an
error or a shutdown occurs.
This patch should fix the issue #1522. It must be backported as far as
2.0. Note that the 2.2 and 2.0 are not affected by this bug because there is
no owner for backend TCP connections. But it is probably a good idea to
backport the patch on these versions to avoid any future bugs.
Tim Duesterhus [Fri, 4 Mar 2022 23:52:45 +0000 (00:52 +0100)]
CLEANUP: fcgi: Use `istadv()` in `fcgi_strm_send_params`
Found manually, while creating the previous commits to turn `struct proxy`
members into ists.
There is an existing Coccinelle rule to replace this pattern by `istadv()` in
`ist.cocci`:
@@
struct ist i;
expression e;
@@
- i.ptr += e;
- i.len -= e;
+ i = istadv(i, e);
But apparently it is not smart enough to match ists that are stored in another
struct. It would be useful to make the existing rule more generic, so that it
might catch similar cases in the future.
BUG/MEDIUM: mcli: Properly handle errors and timeouts during reponse processing
The response analyzer of the master CLI only handles read errors. So if
there is a write error, the session remains stuck because some outgoing data
are blocked in the channel and the response analyzer waits everything to be
sent. Because the maxconn is set to 10 for the master CLI, it may be
unresponsive if this happens to many times.
Now read and write errors, timeouts and client aborts are handled.
This patch should solve the issue #1512. It must be backported as far as
2.0.
DEBUG: cache: Update underlying buffer when loading HTX message in cache applet
In the I/O handler of the cache applet, we must update the underlying buffer
when the HTX message is loaded, using htx_from_buf() function instead of
htxbuf(). It is important because the applet will update the message by
adding new HTX blocks. This way, the state of the underlying buffer remains
consistant with the state of the HTX message.
It is especially important if HAProxy is compiled with "DEBUG_STRICT=2"
mode. Without this patch, channel_add_input() call crashed if the channel
was empty at the begining of the I/O handler.
Note that it is more a build/debug issue than a bug. But this patch may
prevent future bugs. For now it is safe because htx_to_buf() function is
systematically called, updating accordingly the underlying buffer.
BUG/MEDIUM: stream: Use the front analyzers for new listener-less streams
For now, for a stream, request analyzers are set at 2 stages. The first one
is when the stream is created. The session's listener analyzers, if any, are
set on the request channel. In addition, some HTTP analyzers are set for HTX
streams (AN_REQ_WAIT_HTTP and AN_REQ_HTTP_PROCESS_FE). The second one is
when the backend is set on the stream. At the stage, request analyzers are
updated using the backend settings.
It is an issue for client applets because there is no listener attached to
the stream. In addtion, it may have no specific/dedicated backend. Thus,
several request analyzers are missing. Among others, the HTTP analyzers for
HTTP applets. The HTTP client is the only one affected for now.
To fix the bug, when a stream is created without a listener, we use the
frontend to set the request analyzers. Note that there is no issue with the
response channel because its analyzers are set when the server connection is
established.
This patch may be backported to all stable versions. Because only the HTTP
client is affected, it must at least be backported to 2.5. It is related to
the issue #1593.
BUG/MINOR: cache: Set conn-stream/channel EOI flags at the end of request
This bug is the same than for the HTTP client. See "BUG/MINOR: httpclient:
Set conn-stream/channel EOI flags at the end of request" for details.
Note that because a filter is always attached to the stream when the cache
is used, there is no issue because there is no direct forwarding in this
case. Thus the stream analyzers are able to see the HTX_FL_EOM flag on the
HTX messge.
This patch must be backported as far as 2.0. But only CF_EOI must be set
because applets are not attached to a conn-stream on older versions.
BUG/MINOR: httpclient: Set conn-stream/channel EOI flags at the end of request
In HTX, HTX_FL_EOM flag is added on the message to notifiy the end of the
message was received. In addition, the producer must set CS_FL_EOI flag on
the conn-stream. If it is a mux, the stream-interface is responsible to set
CF_EOI flag on the input channel. But, for now, if the producer is an
applet, in addition to the conn-stream flag, it must also set the channel
one.
These flags are used to notify the stream that the message is finished and
no more data are expected. It is especially important when the message
itself it directly forwarded from one side to the other. Because in this
case, the stream has no way to see the HTX_FL_EOM flag on the
message. Otherwise, the stream will detect a client or a server abort,
depending on the side.
For the HTTP client, it is not really easy to diagnose this error because
there is also another bug hiding this one. All HTTP request analyzers are
not set on the input channel. This will be fixed by another patch.
This patch must be backported to 2.5. It is related to the issue #1593.
David Carlier [Tue, 8 Mar 2022 14:49:29 +0000 (14:49 +0000)]
BUILD: fix recent build breakage of freebsd caused by kFreeBSD build fix
Supporting kFreebsd previously led to FreeBSD (< 14) build breakage:
In file included from src/cpuset.c:5:
In file included from include/haproxy/cpuset.h:4:
include/haproxy/cpuset-t.h:46:2: error: unknown type name 'cpu_set_t'; did you mean 'cpuset_t'?
CPUSET_REPR cpuset;
^~~~~~~~~~~
cpuset_t
include/haproxy/cpuset-t.h:21:22: note: expanded from macro 'CPUSET_REPR'
# define CPUSET_REPR cpu_set_t
^
Marno Krahmer [Tue, 8 Mar 2022 12:45:09 +0000 (13:45 +0100)]
MINOR: stats: Add dark mode support for socket rows
In commit e9ed63e548 dark mode support was added to the stats page. The
initial commit does not include dark mode color overwrites for the
.socket CSS class. This commit colors socket rows the same way as
backends that acre active but do not have a health check defined.
This fixes an issue where reading information from socket lines became
really hard in dark mode due to suboptimal coloring of the cell
background and the font in it.
BUG/MEDIUM: quic: do not drop packet on duplicate stream/decoding error
Change the return value to success in qc_handle_bidi_strm_frm for two
specific cases :
* if STREAM frame is an already received offset
* if application decoding failed
This ensures that the packet is not dropped and properly acknowledged.
Previous to this fix, the return code was set to error which prevented
the ACK to be generated.
The impact of the bug might be noticeable in environment with packet
loss and retransmission. Due to haproxy not generating ACK for packets
containing STREAM frames with already received offset, the client will
probably retransmit them again, which will worsen the network
transmission.
Willy Tarreau [Tue, 8 Mar 2022 09:41:40 +0000 (10:41 +0100)]
MINOR: pools: add a new global option "no-memory-trimming"
Some users with very large numbers of connections have been facing
extremely long malloc_trim() calls on reload that managed to trigger
the watchdog! That's a bit counter-productive. It's even possible
that some implementations are not perfectly reliable or that their
trimming time grows quadratically with the memory used. Instead of
constantly trying to work around these issues, let's offer an option
to disable this mechanism, since nobody had been complaining in the
past, and this was only meant to be an improvement.
This should be backported to 2.4 where trimming on reload started to
appear.
BUG/MAJOR: quic: Wrong quic_max_available_room() returned value
Around limits for QUIC integer encoding, this functions could return
wrong values which lead to qc_build_frms() to prepare wrong CRYPTO (less chances)
or STREAM frames (more chances). qc_do_build_pkt() could build wrong packets
with bad CRYPTO/STREAM frames which could not be decoded by the peer.
In such a case ngtcp2 closes the connection with an ENCRYPTION_ERROR error
in a transport CONNECTION_CLOSE frame.
This function returns the maximum integer which may be encoded with a number of
bytes passed as parameter. Useful to precisely compute the number of bytes which
may used to fulfill a buffer with lengths as QUIC enteger encoded prefixes for the
number of following bytes.
MINOR: quic: More precise window update calculation
When in congestion avoidance state and when acknowledging an <acked> number bytes
we must increase the congestion window by at most one datagram (<path->mtu>)
by congestion window. So thanks to this patch we apply a ratio to the current
number of acked bytes : <acked> * <path->mtu> / <cwnd>.
So, when <cwnd> bytes are acked we precisely increment <cwnd> by <path->mtu>.
Furthermore we take into an account the number of remaining acknowledged bytes
each time we increment the window by <acked> storing their values in the algorithm
struct state (->remain_acked) so that it might be take into an account at the
next ACK event.
BUG/MINOR: quic: Confusion betwen "in_flight" and "prep_in_flight" in quic_path_prep_data()
This function returns the remaining number of bytes which can be sent on the
network before fulfilling the congestion window. There is a counter for
the number of prepared data and another one for the really in flight number
of bytes (in_flight). These variable have been mixed up.
CLEANUP: quic: Remove useless definitions from quic_cc_event struct
Since the persistent congestion detection is done out of the congestion
controllers, there is no need to pass them information through quic_cc_event struct.
We remove its useless members. Also remove qc_cc_loss_event() which is no more used.
MINOR: quic: Persistent congestion detection outside of controllers
We establish the persistent congestion out of any congestion controller
to improve the algorithms genericity. This path characteristic detection may
be implemented regarless of the underlying congestion control algorithm.
Send congestion (loss) event using directly quic_cc_event(), so without
qc_cc_loss_event() wrapper function around quic_cc_event().
Take the opportunity of this patch to shorten "newest_time_sent" member field
of quic_cc_event to "time_sent".
MINOR: quic: Add a "slow start" callback to congestion controller
We want to be able to make the congestion controllers re-enter the slow
start state outside of the congestion controllers themselves. So,
we add a callback ->slow_start() to do so.
Define this callback for NewReno algorithm.
CLEANUP: quic: Remove QUIC path manipulations out of the congestion controller
QUIC connection path in flight bytes is a variable which should not be manipulated
by the congestion controller. This latter aim is to compute the congestion window.
So, we pass it as less as parameters as possible to do so.
David Carlier [Fri, 4 Mar 2022 15:50:48 +0000 (15:50 +0000)]
BUILD: fix kFreeBSD build.
kFreeBSD needs to be treated as a distinct target from FreeBSD
since the underlying system libc is the GNU one. Thus, relying
only on __GLIBC__ no longer suffice.
- freebsd-glibc new target, key difference is including crypt.h
and linking to libdl like linux.
- cpu affinity available but the api is still the FreeBSD's.
- enabling auxiliary data access only for Linux.
MEDIUM: mux-quic: implement MAX_STREAMS emission for bidir streams
Implement the locally flow-control streams limit for opened
bidirectional streams. Add a counter which is used to count the total
number of closed streams. If this number is big enough, emit a
MAX_STREAMS frame to increase the limit of remotely opened bidirectional
streams.
This is the first commit to implement QUIC flow-control. A series of
patches should follow to complete this.
This is required to be able to handle more than 100 client requests.
This should help to validate the Multiplexing interop test.
MINOR: mux-quic: retry send opportunistically for remaining frames
This commit should fix the possible transfer interruption caused by the
previous commit. The MUX always retry to send frames if there is
remaining data after a send call on the transport layer. This is useful
if the transport layer is not blocked on the sending path.
In the future, the transport layer should retry by itself the send
operation if no blocking condition exists. The MUX layer will always
subscribe to retry later if remaining frames are reported which indicate
a blocking on the transport layer.
MEDIUM: mux-quic: use direct send transport API for STREAMs
Modify the STREAM emission in qc_send. Use the new transport function
qc_send_app_pkts to directly send the list of constructed frames. This
allows to remove the tasklet wakeup on the quic_conn and should reduce
the latency.
If not all frames are send after the transport call, subscribe the MUX
on the lower layer to be able to retry. Currently there is a bug because
the transport layer does not retry to send frames in excess after a
successful sendto. This might cause the transfer to be interrupted.
MINOR: mux-quic: define new unions for flow-control fields
Define two new unions in the qcc structure named 'lfctl' and 'rfctl'.
For the moment they are empty. They will be completed to store the
initial and current level for flow-control on the local and remote side.
MINOR: mux-quic: complete functions to detect stream type
Improve the functions used to detect the stream characteristics :
uni/bidirectional and local/remote initiated.
Most notably, these functions are now designed to work transparently for
a MUX in the frontend or backend side. For this, we use the connection
to determine the current MUX side. This will be useful if QUIC is
implemented on the server side.
MINOR: mux-quic: refactor transport parameters init
Since QUIC accept handling has been improved, the MUX is initialized
after the handshake completion. Thus its safe to access transport
parameters in qc_init via the quic_conn.
Remove quic_mux_transport_params_update which was called by the
transport for the MUX. This improves the architecture by removing a
direct call from the transport to the MUX.
The deleted function body is not transfered to qc_init because this part
will change heavily in the near future when implementing the
flow-control.