summaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/haproxy/action-t.h13
-rw-r--r--include/haproxy/applet-t.h32
-rw-r--r--include/haproxy/applet.h249
-rw-r--r--include/haproxy/atomic.h20
-rw-r--r--include/haproxy/backend-t.h15
-rw-r--r--include/haproxy/backend.h10
-rw-r--r--include/haproxy/buf.h204
-rw-r--r--include/haproxy/bug.h109
-rw-r--r--include/haproxy/cbuf-t.h1
-rw-r--r--include/haproxy/cfgparse.h1
-rw-r--r--include/haproxy/channel.h76
-rw-r--r--include/haproxy/cli-t.h28
-rw-r--r--include/haproxy/compat.h12
-rw-r--r--include/haproxy/connection-t.h83
-rw-r--r--include/haproxy/connection.h34
-rw-r--r--include/haproxy/counters-t.h15
-rw-r--r--include/haproxy/defaults.h48
-rw-r--r--include/haproxy/dgram-t.h2
-rw-r--r--include/haproxy/dns-t.h14
-rw-r--r--include/haproxy/dns_ring-t.h110
-rw-r--r--include/haproxy/dns_ring.h46
-rw-r--r--include/haproxy/dynbuf-t.h73
-rw-r--r--include/haproxy/dynbuf.h150
-rw-r--r--include/haproxy/fcgi-app-t.h5
-rw-r--r--include/haproxy/filters-t.h1
-rw-r--r--include/haproxy/freq_ctr.h8
-rw-r--r--include/haproxy/global-t.h10
-rw-r--r--include/haproxy/global.h1
-rw-r--r--include/haproxy/guid-t.h15
-rw-r--r--include/haproxy/guid.h16
-rw-r--r--include/haproxy/h1.h1
-rw-r--r--include/haproxy/h3.h40
-rw-r--r--include/haproxy/http.h17
-rw-r--r--include/haproxy/http_ana-t.h4
-rw-r--r--include/haproxy/http_client-t.h1
-rw-r--r--include/haproxy/http_client.h1
-rw-r--r--include/haproxy/http_htx-t.h9
-rw-r--r--include/haproxy/htx-t.h17
-rw-r--r--include/haproxy/intops.h126
-rw-r--r--include/haproxy/jwt-t.h1
-rw-r--r--include/haproxy/lb_ss-t.h32
-rw-r--r--include/haproxy/lb_ss.h33
-rw-r--r--include/haproxy/linuxcap.h1
-rw-r--r--include/haproxy/list.h7
-rw-r--r--include/haproxy/listener-t.h9
-rw-r--r--include/haproxy/listener.h7
-rw-r--r--include/haproxy/log-t.h120
-rw-r--r--include/haproxy/log.h51
-rw-r--r--include/haproxy/mqtt-t.h1
-rw-r--r--include/haproxy/mux_h1-t.h23
-rw-r--r--include/haproxy/mux_quic-t.h46
-rw-r--r--include/haproxy/mux_quic.h13
-rw-r--r--include/haproxy/net_helper.h28
-rw-r--r--include/haproxy/openssl-compat.h28
-rw-r--r--include/haproxy/pattern-t.h8
-rw-r--r--include/haproxy/peers-t.h102
-rw-r--r--include/haproxy/peers.h22
-rw-r--r--include/haproxy/pool.h4
-rw-r--r--include/haproxy/proto_quic.h4
-rw-r--r--include/haproxy/proto_rhttp.h1
-rw-r--r--include/haproxy/proto_sockpair.h2
-rw-r--r--include/haproxy/proto_udp.h2
-rw-r--r--include/haproxy/protobuf.h10
-rw-r--r--include/haproxy/protocol-t.h11
-rw-r--r--include/haproxy/proxy-t.h42
-rw-r--r--include/haproxy/proxy.h14
-rw-r--r--include/haproxy/qmux_http.h1
-rw-r--r--include/haproxy/qpack-dec.h17
-rw-r--r--include/haproxy/qpack-t.h7
-rw-r--r--include/haproxy/qpack-tbl-t.h2
-rw-r--r--include/haproxy/queue-t.h1
-rw-r--r--include/haproxy/quic_ack-t.h4
-rw-r--r--include/haproxy/quic_ack.h6
-rw-r--r--include/haproxy/quic_cc-t.h6
-rw-r--r--include/haproxy/quic_cc_hystart.h129
-rw-r--r--include/haproxy/quic_conn-t.h11
-rw-r--r--include/haproxy/quic_conn.h2
-rw-r--r--include/haproxy/quic_fctl-t.h15
-rw-r--r--include/haproxy/quic_fctl.h19
-rw-r--r--include/haproxy/quic_rx-t.h7
-rw-r--r--include/haproxy/quic_rx.h2
-rw-r--r--include/haproxy/quic_sock-t.h4
-rw-r--r--include/haproxy/quic_sock.h8
-rw-r--r--include/haproxy/quic_ssl.h4
-rw-r--r--include/haproxy/quic_tls-t.h11
-rw-r--r--include/haproxy/quic_tls.h10
-rw-r--r--include/haproxy/quic_tx-t.h3
-rw-r--r--include/haproxy/quic_tx.h10
-rw-r--r--include/haproxy/receiver-t.h1
-rw-r--r--include/haproxy/resolvers-t.h5
-rw-r--r--include/haproxy/resolvers.h2
-rw-r--r--include/haproxy/ring-t.h59
-rw-r--r--include/haproxy/ring.h81
-rw-r--r--include/haproxy/sample.h1
-rw-r--r--include/haproxy/sc_strm.h35
-rw-r--r--include/haproxy/server-t.h73
-rw-r--r--include/haproxy/server.h33
-rw-r--r--include/haproxy/session-t.h17
-rw-r--r--include/haproxy/session.h79
-rw-r--r--include/haproxy/shctx.h2
-rw-r--r--include/haproxy/sink-t.h3
-rw-r--r--include/haproxy/sink.h16
-rw-r--r--include/haproxy/sock.h35
-rw-r--r--include/haproxy/ssl_ckch-t.h39
-rw-r--r--include/haproxy/ssl_ckch.h21
-rw-r--r--include/haproxy/ssl_crtlist.h2
-rw-r--r--include/haproxy/ssl_gencert.h35
-rw-r--r--include/haproxy/ssl_ocsp-t.h6
-rw-r--r--include/haproxy/ssl_ocsp.h3
-rw-r--r--include/haproxy/ssl_sock-t.h12
-rw-r--r--include/haproxy/ssl_sock.h14
-rw-r--r--include/haproxy/stats-file-t.h12
-rw-r--r--include/haproxy/stats-file.h25
-rw-r--r--include/haproxy/stats-html-t.h21
-rw-r--r--include/haproxy/stats-html.h22
-rw-r--r--include/haproxy/stats-json.h24
-rw-r--r--include/haproxy/stats-proxy.h14
-rw-r--r--include/haproxy/stats-t.h506
-rw-r--r--include/haproxy/stats.h69
-rw-r--r--include/haproxy/stconn-t.h38
-rw-r--r--include/haproxy/stconn.h115
-rw-r--r--include/haproxy/stick_table-t.h16
-rw-r--r--include/haproxy/stick_table.h63
-rw-r--r--include/haproxy/stream-t.h13
-rw-r--r--include/haproxy/stream.h2
-rw-r--r--include/haproxy/systemd.h7
-rw-r--r--include/haproxy/task-t.h18
-rw-r--r--include/haproxy/tcpcheck-t.h26
-rw-r--r--include/haproxy/tcpcheck.h4
-rw-r--r--include/haproxy/thread-t.h55
-rw-r--r--include/haproxy/thread.h56
-rw-r--r--include/haproxy/tinfo-t.h22
-rw-r--r--include/haproxy/tools-t.h18
-rw-r--r--include/haproxy/tools.h57
-rw-r--r--include/haproxy/vars.h5
-rw-r--r--include/haproxy/vecpair.h588
-rw-r--r--include/haproxy/version.h4
-rw-r--r--include/haproxy/xref.h1
-rw-r--r--include/import/ebtree.h136
-rw-r--r--include/import/ist.h19
-rw-r--r--include/import/slz-tables.h2
-rw-r--r--include/import/xxhash.h2
-rw-r--r--include/make/options.mk23
-rw-r--r--include/make/verbose.mk3
144 files changed, 4035 insertions, 1043 deletions
diff --git a/include/haproxy/action-t.h b/include/haproxy/action-t.h
index f77bdce..eee16a3 100644
--- a/include/haproxy/action-t.h
+++ b/include/haproxy/action-t.h
@@ -25,6 +25,7 @@
#include <haproxy/applet-t.h>
#include <haproxy/stick_table-t.h>
#include <haproxy/vars-t.h>
+#include <haproxy/log-t.h>
struct session;
struct stream;
@@ -141,15 +142,15 @@ struct act_rule {
struct {
int i; /* integer param (status, nice, loglevel, ..) */
struct ist str; /* string param (reason, header name, ...) */
- struct list fmt; /* log-format compatible expression */
+ struct lf_expr fmt; /* log-format compatible expression */
struct my_regex *re; /* used by replace-header/value/uri/path */
} http; /* args used by some HTTP rules */
struct http_reply *http_reply; /* HTTP response to be used by return/deny/tarpit rules */
struct redirect_rule *redir; /* redirect rule or "http-request redirect" */
struct {
char *ref; /* MAP or ACL file name to update */
- struct list key; /* pattern to retrieve MAP or ACL key */
- struct list value; /* pattern to retrieve MAP value */
+ struct lf_expr key; /* pattern to retrieve MAP or ACL key */
+ struct lf_expr value; /* pattern to retrieve MAP value */
} map;
struct sample_expr *expr;
struct {
@@ -167,7 +168,7 @@ struct act_rule {
} timeout;
struct hlua_rule *hlua_rule;
struct {
- struct list fmt; /* log-format compatible expression */
+ struct lf_expr fmt; /* log-format compatible expression */
struct sample_expr *expr;
uint64_t name_hash;
enum vars_scope scope;
@@ -192,6 +193,10 @@ struct act_rule {
struct sample_expr *name; /* used to differentiate idle connections */
} attach_srv; /* 'attach-srv' rule */
struct {
+ int value;
+ struct sample_expr *expr;
+ } expr_int; /* expr or int value (when expr is NULL)*/
+ struct {
void *p[4];
} act; /* generic pointers to be used by custom actions */
} arg; /* arguments used by some actions */
diff --git a/include/haproxy/applet-t.h b/include/haproxy/applet-t.h
index bd96403..a305da6 100644
--- a/include/haproxy/applet-t.h
+++ b/include/haproxy/applet-t.h
@@ -27,13 +27,29 @@
#include <haproxy/dynbuf-t.h>
#include <haproxy/freq_ctr-t.h>
#include <haproxy/obj_type-t.h>
+#include <haproxy/task-t.h>
#include <haproxy/xref-t.h>
/* flags for appctx->state */
-#define APPLET_WANT_DIE 0x01 /* applet was running and requested to die */
/* Room for per-command context (mostly CLI commands but not only) */
-#define APPLET_MAX_SVCCTX 88
+#define APPLET_MAX_SVCCTX 128
+
+/* Appctx Flags */
+#define APPCTX_FL_INBLK_ALLOC 0x00000001
+#define APPCTX_FL_INBLK_FULL 0x00000002
+#define APPCTX_FL_OUTBLK_ALLOC 0x00000004
+#define APPCTX_FL_OUTBLK_FULL 0x00000008
+#define APPCTX_FL_EOI 0x00000010
+#define APPCTX_FL_EOS 0x00000020
+#define APPCTX_FL_ERR_PENDING 0x00000040
+#define APPCTX_FL_ERROR 0x00000080
+#define APPCTX_FL_SHUTDOWN 0x00000100 /* applet was shut down (->release() called if any). No more data exchange with SCs */
+#define APPCTX_FL_WANT_DIE 0x00000200 /* applet was running and requested to die */
+#define APPCTX_FL_INOUT_BUFS 0x00000400 /* applet uses its own buffers */
+#define APPCTX_FL_FASTFWD 0x00000800 /* zero-copy forwarding is in-use, don't fill the outbuf */
+#define APPCTX_FL_IN_MAYALLOC 0x00001000 /* applet may try again to allocate its inbuf */
+#define APPCTX_FL_OUT_MAYALLOC 0x00002000 /* applet may try again to allocate its outbuf */
struct appctx;
struct proxy;
@@ -49,6 +65,9 @@ struct applet {
int (*init)(struct appctx *); /* callback to init resources, may be NULL.
expect 0 if ok, -1 if an error occurs. */
void (*fct)(struct appctx *); /* internal I/O handler, may never be NULL */
+ size_t (*rcv_buf)(struct appctx *appctx, struct buffer *buf, size_t count, unsigned int flags); /* called from the upper layer to get data */
+ size_t (*snd_buf)(struct appctx *appctx, struct buffer *buf, size_t count, unsigned int flags); /* Called from the upper layet to put data */
+ size_t (*fastfwd)(struct appctx *appctx, struct buffer *buf, size_t count, unsigned int flags); /* Callback to fast-forward data */
void (*release)(struct appctx *); /* callback to release resources, may be NULL */
unsigned int timeout; /* execution timeout. */
};
@@ -57,9 +76,14 @@ struct applet {
struct appctx {
enum obj_type obj_type; /* OBJ_TYPE_APPCTX */
/* 3 unused bytes here */
- unsigned short state; /* Internal appctx state */
unsigned int st0; /* CLI state for stats, session state for peers */
unsigned int st1; /* prompt/payload (bitwise OR of APPCTX_CLI_ST1_*) for stats, session error for peers */
+
+ unsigned int flags; /* APPCTX_FL_* */
+ struct buffer inbuf;
+ struct buffer outbuf;
+ size_t to_forward;
+
struct buffer *chunk; /* used to store unfinished commands */
struct applet *applet; /* applet this context refers to */
struct session *sess; /* session for frontend applets (NULL for backend applets) */
@@ -75,7 +99,7 @@ struct appctx {
struct buffer_wait buffer_wait; /* position in the list of objects waiting for a buffer */
struct task *t; /* task associated to the applet */
struct freq_ctr call_rate; /* appctx call rate */
- struct list wait_entry; /* entry in a list of waiters for an event (e.g. ring events) */
+ struct mt_list wait_entry; /* entry in a list of waiters for an event (e.g. ring events) */
/* The pointer seen by application code is appctx->svcctx. In 2.7 the
* anonymous union and the "ctx" struct disappeared, and the struct
diff --git a/include/haproxy/applet.h b/include/haproxy/applet.h
index b04ffd9..1c9721d 100644
--- a/include/haproxy/applet.h
+++ b/include/haproxy/applet.h
@@ -38,6 +38,7 @@ extern unsigned int nb_applets;
extern struct pool_head *pool_head_appctx;
struct task *task_run_applet(struct task *t, void *context, unsigned int state);
+struct task *task_process_applet(struct task *t, void *context, unsigned int state);
int appctx_buf_available(void *arg);
void *applet_reserve_svcctx(struct appctx *appctx, size_t size);
void applet_reset_svcctx(struct appctx *appctx);
@@ -48,6 +49,19 @@ int appctx_finalize_startup(struct appctx *appctx, struct proxy *px, struct buff
void appctx_free_on_early_error(struct appctx *appctx);
void appctx_free(struct appctx *appctx);
+size_t appctx_htx_rcv_buf(struct appctx *appctx, struct buffer *buf, size_t count, unsigned int flags);
+size_t appctx_raw_rcv_buf(struct appctx *appctx, struct buffer *buf, size_t count, unsigned int flags);
+size_t appctx_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, unsigned int flags);
+
+size_t appctx_htx_snd_buf(struct appctx *appctx, struct buffer *buf, size_t count, unsigned int flags);
+size_t appctx_raw_snd_buf(struct appctx *appctx, struct buffer *buf, size_t count, unsigned int flags);
+size_t appctx_snd_buf(struct stconn *sc, struct buffer *buf, size_t count, unsigned int flags);
+
+int appctx_fastfwd(struct stconn *sc, unsigned int count, unsigned int flags);
+ssize_t applet_append_line(void *ctx, struct ist v1, struct ist v2, size_t ofs, size_t len);
+static forceinline void applet_fl_set(struct appctx *appctx, uint on);
+static forceinline void applet_fl_clr(struct appctx *appctx, uint off);
+
static inline struct appctx *appctx_new_here(struct applet *applet, struct sedesc *sedesc)
{
return appctx_new_on(applet, sedesc, tid);
@@ -58,6 +72,41 @@ static inline struct appctx *appctx_new_anywhere(struct applet *applet, struct s
return appctx_new_on(applet, sedesc, -1);
}
+
+/*
+ * Release a buffer, if any, and try to wake up entities waiting in the buffer
+ * wait queue.
+ */
+static inline void appctx_release_buf(struct appctx *appctx, struct buffer *bptr)
+{
+ if (bptr->size) {
+ b_free(bptr);
+ offer_buffers(appctx->buffer_wait.target, 1);
+ }
+}
+
+/*
+ * Allocate a buffer. If if fails, it adds the appctx in buffer wait queue and
+ * sets the relevant blocking flag depending on the side (assuming that bptr is
+ * either &appctx->inbuf or &appctx->outbuf). Upon success it will also clear
+ * the equivalent MAYALLOC flags.
+ */
+static inline struct buffer *appctx_get_buf(struct appctx *appctx, struct buffer *bptr)
+{
+ struct buffer *buf = NULL;
+ int is_inbuf = (bptr == &appctx->inbuf);
+
+ if (likely(!LIST_INLIST(&appctx->buffer_wait.list))) {
+ if (unlikely((buf = b_alloc(bptr, is_inbuf ? DB_MUX_TX : DB_SE_RX)) == NULL)) {
+ b_queue(is_inbuf ? DB_MUX_TX : DB_SE_RX, &appctx->buffer_wait, appctx, appctx_buf_available);
+ applet_fl_set(appctx, is_inbuf ? APPCTX_FL_INBLK_ALLOC : APPCTX_FL_OUTBLK_ALLOC);
+ } else {
+ applet_fl_clr(appctx, is_inbuf ? APPCTX_FL_IN_MAYALLOC : APPCTX_FL_OUT_MAYALLOC);
+ }
+ }
+ return buf;
+}
+
/* Helper function to call .init applet callback function, if it exists. Returns 0
* on success and -1 on error.
*/
@@ -78,9 +127,11 @@ static inline int appctx_init(struct appctx *appctx)
/* Releases an appctx previously allocated by appctx_new(). */
static inline void __appctx_free(struct appctx *appctx)
{
+ appctx_release_buf(appctx, &appctx->inbuf);
+ appctx_release_buf(appctx, &appctx->outbuf);
+
task_destroy(appctx->t);
- if (LIST_INLIST(&appctx->buffer_wait.list))
- LIST_DEL_INIT(&appctx->buffer_wait.list);
+ b_dequeue(&appctx->buffer_wait);
if (appctx->sess)
session_free(appctx->sess);
BUG_ON(appctx->sedesc && !se_fl_test(appctx->sedesc, SE_FL_ORPHAN));
@@ -109,6 +160,67 @@ static inline struct stream *appctx_strm(const struct appctx *appctx)
return __sc_strm(appctx->sedesc->sc);
}
+/* returns 1 if the appctx is attached on the backend side or 0 if it is
+ * attached on the frontend side. Note that only frontend appctx may have no SC.
+ */
+static inline int appctx_is_back(const struct appctx *appctx)
+{
+ struct stconn *sc = appctx_sc(appctx);
+
+ return !!(sc && (sc->flags & SC_FL_ISBACK));
+}
+
+static forceinline void applet_fl_zero(struct appctx *appctx)
+{
+ appctx->flags = 0;
+}
+
+static forceinline void applet_fl_setall(struct appctx *appctx, uint all)
+{
+ appctx->flags = all;
+}
+
+static forceinline void applet_fl_set(struct appctx *appctx, uint on)
+{
+ if (((on & (APPCTX_FL_EOS|APPCTX_FL_EOI)) && appctx->flags & APPCTX_FL_ERR_PENDING) ||
+ ((on & APPCTX_FL_ERR_PENDING) && appctx->flags & (APPCTX_FL_EOI|APPCTX_FL_EOS)))
+ on |= APPCTX_FL_ERROR;
+ appctx->flags |= on;
+}
+
+static forceinline void applet_fl_clr(struct appctx *appctx, uint off)
+{
+ appctx->flags &= ~off;
+}
+
+static forceinline uint applet_fl_test(const struct appctx *appctx, uint test)
+{
+ return !!(appctx->flags & test);
+}
+
+static forceinline uint applet_fl_get(const struct appctx *appctx)
+{
+ return appctx->flags;
+}
+
+static inline void applet_set_eoi(struct appctx *appctx)
+{
+ applet_fl_set(appctx, APPCTX_FL_EOI);
+}
+
+static inline void applet_set_eos(struct appctx *appctx)
+{
+ applet_fl_set(appctx, APPCTX_FL_EOS);
+}
+
+static inline void applet_set_error(struct appctx *appctx)
+{
+ if (applet_fl_test(appctx, (APPCTX_FL_EOS|APPCTX_FL_EOI)))
+ applet_fl_set(appctx, APPCTX_FL_ERROR);
+ else
+ applet_fl_set(appctx, APPCTX_FL_ERR_PENDING);
+}
+
/* The applet announces it has more data to deliver to the stream's input
* buffer.
*/
@@ -173,20 +285,32 @@ static inline void applet_expect_data(struct appctx *appctx)
*/
static inline int applet_putchk(struct appctx *appctx, struct buffer *chunk)
{
- struct sedesc *se = appctx->sedesc;
int ret;
- ret = ci_putchk(sc_ic(se->sc), chunk);
- if (ret < 0) {
- /* XXX: Handle all errors as a lack of space because callers
- * don't handles other cases for now. So applets must be
- * careful to handles shutdown (-2) and invalid calls (-3) by
- * themselves.
- */
- sc_need_room(se->sc, chunk->data);
- ret = -1;
+ if (appctx->flags & APPCTX_FL_INOUT_BUFS) {
+ if (b_data(chunk) > b_room(&appctx->outbuf)) {
+ applet_fl_set(appctx, APPCTX_FL_OUTBLK_FULL);
+ ret = -1;
+ }
+ else {
+ ret = b_putblk(&appctx->outbuf, b_head(chunk), b_data(chunk));
+ chunk->data -= ret;
+ }
+ }
+ else {
+ struct sedesc *se = appctx->sedesc;
+
+ ret = ci_putchk(sc_ic(se->sc), chunk);
+ if (ret < 0) {
+ /* XXX: Handle all errors as a lack of space because callers
+ * don't handles other cases for now. So applets must be
+ * careful to handles shutdown (-2) and invalid calls (-3) by
+ * themselves.
+ */
+ sc_need_room(se->sc, chunk->data);
+ ret = -1;
+ }
}
-
return ret;
}
@@ -196,18 +320,29 @@ static inline int applet_putchk(struct appctx *appctx, struct buffer *chunk)
*/
static inline int applet_putblk(struct appctx *appctx, const char *blk, int len)
{
- struct sedesc *se = appctx->sedesc;
int ret;
- ret = ci_putblk(sc_ic(se->sc), blk, len);
- if (ret < -1) {
- /* XXX: Handle all errors as a lack of space because callers
- * don't handles other cases for now. So applets must be
- * careful to handles shutdown (-2) and invalid calls (-3) by
- * themselves.
- */
- sc_need_room(se->sc, len);
- ret = -1;
+ if (appctx->flags & APPCTX_FL_INOUT_BUFS) {
+ if (len > b_room(&appctx->outbuf)) {
+ applet_fl_set(appctx, APPCTX_FL_OUTBLK_FULL);
+ ret = -1;
+ }
+ else
+ ret = b_putblk(&appctx->outbuf, blk, len);
+ }
+ else {
+ struct sedesc *se = appctx->sedesc;
+
+ ret = ci_putblk(sc_ic(se->sc), blk, len);
+ if (ret < 0) {
+ /* XXX: Handle all errors as a lack of space because callers
+ * don't handles other cases for now. So applets must be
+ * careful to handles shutdown (-2) and invalid calls (-3) by
+ * themselves.
+ */
+ sc_need_room(se->sc, len);
+ ret = -1;
+ }
}
return ret;
@@ -220,20 +355,32 @@ static inline int applet_putblk(struct appctx *appctx, const char *blk, int len)
*/
static inline int applet_putstr(struct appctx *appctx, const char *str)
{
- struct sedesc *se = appctx->sedesc;
int ret;
- ret = ci_putstr(sc_ic(se->sc), str);
- if (ret == -1) {
- /* XXX: Handle all errors as a lack of space because callers
- * don't handles other cases for now. So applets must be
- * careful to handles shutdown (-2) and invalid calls (-3) by
- * themselves.
- */
- sc_need_room(se->sc, strlen(str));
- ret = -1;
- }
+ if (appctx->flags & APPCTX_FL_INOUT_BUFS) {
+ int len = strlen(str);
+ if (len > b_room(&appctx->outbuf)) {
+ applet_fl_set(appctx, APPCTX_FL_OUTBLK_FULL);
+ ret = -1;
+ }
+ else
+ ret = b_putblk(&appctx->outbuf, str, len);
+ }
+ else {
+ struct sedesc *se = appctx->sedesc;
+
+ ret = ci_putstr(sc_ic(se->sc), str);
+ if (ret < 0) {
+ /* XXX: Handle all errors as a lack of space because callers
+ * don't handles other cases for now. So applets must be
+ * careful to handles shutdown (-2) and invalid calls (-3) by
+ * themselves.
+ */
+ sc_need_room(se->sc, strlen(str));
+ ret = -1;
+ }
+ }
return ret;
}
@@ -243,20 +390,32 @@ static inline int applet_putstr(struct appctx *appctx, const char *str)
*/
static inline int applet_putchr(struct appctx *appctx, char chr)
{
- struct sedesc *se = appctx->sedesc;
int ret;
- ret = ci_putchr(sc_ic(se->sc), chr);
- if (ret == -1) {
- /* XXX: Handle all errors as a lack of space because callers
- * don't handles other cases for now. So applets must be
- * careful to handles shutdown (-2) and invalid calls (-3) by
- * themselves.
- */
- sc_need_room(se->sc, 1);
- ret = -1;
+ if (appctx->flags & APPCTX_FL_INOUT_BUFS) {
+ if (b_full(&appctx->outbuf)) {
+ applet_fl_set(appctx, APPCTX_FL_OUTBLK_FULL);
+ ret = -1;
+ }
+ else {
+ b_putchr(&appctx->outbuf, chr);
+ ret = 1;
+ }
+ }
+ else {
+ struct sedesc *se = appctx->sedesc;
+
+ ret = ci_putchr(sc_ic(se->sc), chr);
+ if (ret < 0) {
+ /* XXX: Handle all errors as a lack of space because callers
+ * don't handles other cases for now. So applets must be
+ * careful to handles shutdown (-2) and invalid calls (-3) by
+ * themselves.
+ */
+ sc_need_room(se->sc, 1);
+ ret = -1;
+ }
}
-
return ret;
}
diff --git a/include/haproxy/atomic.h b/include/haproxy/atomic.h
index d64e192..146f2ad 100644
--- a/include/haproxy/atomic.h
+++ b/include/haproxy/atomic.h
@@ -185,6 +185,7 @@
#define __ha_barrier_full() do { } while (0)
#define __ha_compiler_barrier() do { } while (0)
#define __ha_cpu_relax() ({ 1; })
+#define __ha_cpu_relax_for_read() ({ 1; })
#else /* !USE_THREAD */
@@ -198,10 +199,11 @@
#define HA_ATOMIC_LOAD(val) \
({ \
- typeof(*(val)) ret = \
- ({ __sync_synchronize(); *(volatile typeof(val))val; }); \
+ typeof((val)) __val_load = (val); \
+ typeof(*(val)) __ret_val = \
+ ({ __sync_synchronize(); *(volatile typeof(__val_load))__val_load; }); \
__sync_synchronize(); \
- ret; \
+ __ret_val; \
})
#define HA_ATOMIC_STORE(val, new) \
@@ -586,6 +588,9 @@ __ha_cas_dw(void *target, void *compare, const void *set)
/* short-lived CPU relaxation */
#define __ha_cpu_relax() ({ asm volatile("rep;nop\n"); 1; })
+/* dummy relaxation: x86 prefers not to wait at all in read loops */
+#define __ha_cpu_relax_for_read() ({ 1; })
+
#elif defined(__arm__) && (defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__))
static __inline void
@@ -651,6 +656,9 @@ static __inline int __ha_cas_dw(void *target, void *compare, const void *set)
/* short-lived CPU relaxation */
#define __ha_cpu_relax() ({ asm volatile(""); 1; })
+/* short wait in read loops */
+#define __ha_cpu_relax_for_read() ({ asm volatile(""); 1; })
+
#elif defined (__aarch64__)
static __inline void
@@ -697,6 +705,9 @@ __ha_barrier_atomic_full(void)
*/
#define __ha_cpu_relax() ({ asm volatile("isb" ::: "memory"); 1; })
+/* aarch64 prefers to wait for real in read loops */
+#define __ha_cpu_relax_for_read() ({ asm volatile("isb" ::: "memory"); 1; })
+
#if defined(__ARM_FEATURE_ATOMICS) && !defined(__clang__) // ARMv8.1-A atomics
/* returns 0 on failure, non-zero on success */
@@ -799,6 +810,9 @@ static __inline int __ha_cas_dw(void *target, void *compare, void *set)
/* short-lived CPU relaxation */
#define __ha_cpu_relax() ({ asm volatile(""); 1; })
+/* default wait in read loops */
+#define __ha_cpu_relax_for_read() ({ asm volatile(""); 1; })
+
#endif /* end of arch-specific barrier/dwcas */
static inline void __ha_compiler_barrier(void)
diff --git a/include/haproxy/backend-t.h b/include/haproxy/backend-t.h
index 02a2cc5..bc21fd1 100644
--- a/include/haproxy/backend-t.h
+++ b/include/haproxy/backend-t.h
@@ -28,6 +28,7 @@
#include <haproxy/lb_fwlc-t.h>
#include <haproxy/lb_fwrr-t.h>
#include <haproxy/lb_map-t.h>
+#include <haproxy/lb_ss-t.h>
#include <haproxy/server-t.h>
#include <haproxy/thread-t.h>
@@ -58,6 +59,9 @@
#define BE_LB_CB_LC 0x00000000 /* least-connections */
#define BE_LB_CB_FAS 0x00000001 /* first available server (opposite of leastconn) */
+/* BE_LB_SA_* is used with BE_LB_KIND_SA */
+#define BE_LB_SA_SS 0x00000000 /* stick to server as long as it is available */
+
#define BE_LB_PARM 0x000000FF /* mask to get/clear the LB param */
/* Required input(s) */
@@ -73,6 +77,7 @@
#define BE_LB_KIND_RR 0x00010000 /* round-robin */
#define BE_LB_KIND_CB 0x00020000 /* connection-based */
#define BE_LB_KIND_HI 0x00030000 /* hash of input (see hash inputs above) */
+#define BE_LB_KIND_SA 0x00040000 /* standalone (specific algorithms, cannot be grouped) */
#define BE_LB_KIND 0x00070000 /* mask to get/clear LB algorithm */
/* All known variants of load balancing algorithms. These can be cleared using
@@ -83,6 +88,7 @@
#define BE_LB_ALGO_RND (BE_LB_KIND_RR | BE_LB_NEED_NONE | BE_LB_RR_RANDOM) /* random value */
#define BE_LB_ALGO_LC (BE_LB_KIND_CB | BE_LB_NEED_NONE | BE_LB_CB_LC) /* least connections */
#define BE_LB_ALGO_FAS (BE_LB_KIND_CB | BE_LB_NEED_NONE | BE_LB_CB_FAS) /* first available server */
+#define BE_LB_ALGO_SS (BE_LB_KIND_SA | BE_LB_NEED_NONE | BE_LB_SA_SS) /* sticky */
#define BE_LB_ALGO_SRR (BE_LB_KIND_RR | BE_LB_NEED_NONE | BE_LB_RR_STATIC) /* static round robin */
#define BE_LB_ALGO_SH (BE_LB_KIND_HI | BE_LB_NEED_ADDR | BE_LB_HASH_SRC) /* hash: source IP */
#define BE_LB_ALGO_UH (BE_LB_KIND_HI | BE_LB_NEED_HTTP | BE_LB_HASH_URI) /* hash: HTTP URI */
@@ -91,7 +97,6 @@
#define BE_LB_ALGO_RCH (BE_LB_KIND_HI | BE_LB_NEED_DATA | BE_LB_HASH_RDP) /* hash: RDP cookie value */
#define BE_LB_ALGO_SMP (BE_LB_KIND_HI | BE_LB_NEED_DATA | BE_LB_HASH_SMP) /* hash: sample expression */
#define BE_LB_ALGO_LH (BE_LB_KIND_HI | BE_LB_NEED_LOG | BE_LB_HASH_SMP) /* log hash: sample expression */
-#define BE_LB_ALGO_LS (BE_LB_KIND_CB | BE_LB_NEED_LOG | BE_LB_CB_FAS) /* log sticky */
#define BE_LB_ALGO (BE_LB_KIND | BE_LB_NEED | BE_LB_PARM ) /* mask to clear algo */
/* Higher bits define how a given criterion is mapped to a server. In fact it
@@ -147,11 +152,7 @@ struct lbprm {
struct lb_fwlc fwlc;
struct lb_chash chash;
struct lb_fas fas;
- struct {
- struct server **srv; /* array containing in-use log servers */
- struct list avail; /* servers available for lb are registered in this list */
- uint32_t lastid; /* last relative id used */
- } log; /* used in log-balancing context (PR_MODE_SYSLOG backend) */
+ struct lb_ss ss;
};
uint32_t algo; /* load balancing algorithm and variants: BE_LB_* */
int tot_wact, tot_wbck; /* total effective weights of active and backup servers */
@@ -161,7 +162,7 @@ struct lbprm {
int wmult; /* ratio between user weight and effective weight */
int wdiv; /* ratio between effective weight and user weight */
int hash_balance_factor; /* load balancing factor * 100, 0 if disabled */
- struct sample_expr *expr; /* sample expression for "balance hash" */
+ struct sample_expr *expr; /* sample expression for "balance (log-)hash" */
char *arg_str; /* name of the URL parameter/header/cookie used for hashing */
int arg_len; /* strlen(arg_str), computed only once */
int arg_opt1; /* extra option 1 for the LB algo (algo-specific) */
diff --git a/include/haproxy/backend.h b/include/haproxy/backend.h
index 4ab9170..93a4bee 100644
--- a/include/haproxy/backend.h
+++ b/include/haproxy/backend.h
@@ -30,6 +30,15 @@
#include <haproxy/stream-t.h>
#include <haproxy/time.h>
+struct server *get_server_sh(struct proxy *px, const char *addr, int len, const struct server *avoid);
+struct server *get_server_uh(struct proxy *px, char *uri, int uri_len, const struct server *avoid);
+struct server *get_server_ph(struct proxy *px, const char *uri, int uri_len, const struct server *avoid);
+struct server *get_server_ph_post(struct stream *s, const struct server *avoid);
+struct server *get_server_hh(struct stream *s, const struct server *avoid);
+struct server *get_server_rch(struct stream *s, const struct server *avoid);
+struct server *get_server_expr(struct stream *s, const struct server *avoid);
+struct server *get_server_rnd(struct stream *s, const struct server *avoid);
+
int assign_server(struct stream *s);
int assign_server_address(struct stream *s);
int assign_server_and_queue(struct stream *s);
@@ -50,7 +59,6 @@ int tcp_persist_rdp_cookie(struct stream *s, struct channel *req, int an_bit);
int be_downtime(struct proxy *px);
void recount_servers(struct proxy *px);
void update_backend_weight(struct proxy *px);
-int be_lastsession(const struct proxy *be);
/* Returns number of usable servers in backend */
static inline int be_usable_srv(struct proxy *be)
diff --git a/include/haproxy/buf.h b/include/haproxy/buf.h
index e98161e..ad22e6f 100644
--- a/include/haproxy/buf.h
+++ b/include/haproxy/buf.h
@@ -91,6 +91,27 @@ static inline size_t b_full(const struct buffer *b)
return !b_room(b);
}
+/* b_add_ofs() : return new offset within buffer after applying wrapping. Only
+ * offsets resulting from initial positions added to counts within buffer size
+ * limits are handled.
+ */
+static inline size_t b_add_ofs(const struct buffer *b, size_t ofs, size_t count)
+{
+ ofs += count;
+ if (ofs >= b->size)
+ ofs -= b->size;
+ return ofs;
+}
+
+/* b_rel_ofs() : take an absolute offset in the buffer, and return it relative
+ * to the buffer's head for use with b_peek().
+ */
+static inline size_t b_rel_ofs(const struct buffer *b, size_t ofs)
+{
+ if (ofs < b->head)
+ ofs += b->size;
+ return ofs - b->head;
+}
/* b_stop() : returns the pointer to the byte following the end of the buffer,
* which may be out of the buffer if the buffer ends on the last byte of the
@@ -314,6 +335,38 @@ static inline size_t b_contig_space(const struct buffer *b)
return left;
}
+/* b_getblk_ofs() : gets one full block of data at once from a buffer, starting
+ * from offset <offset> after the buffer's area, and for exactly <len> bytes.
+ * As a convenience to avoid complex checks in callers, the offset is allowed
+ * to exceed a valid one by no more than one buffer size, and will automatically
+ * be wrapped. The caller is responsible for ensuring that <len> doesn't exceed
+ * the known length of the available data at this position, otherwise undefined
+ * data will be returned. This is meant to be used on concurrently accessed
+ * buffers, so that a reader can read a known area while the buffer is being fed
+ * and trimmed. The function guarantees never to use ->head nor ->data. The
+ * buffer is left unaffected. It always returns the number of bytes copied.
+ */
+static inline size_t b_getblk_ofs(const struct buffer *buf, char *blk, size_t len, size_t offset)
+{
+ size_t firstblock;
+
+ if (offset >= buf->size)
+ offset -= buf->size;
+
+ BUG_ON(offset >= buf->size);
+
+ firstblock = buf->size - offset;
+
+ if (firstblock >= len)
+ firstblock = len;
+
+ memcpy(blk, b_orig(buf) + offset, firstblock);
+
+ if (len > firstblock)
+ memcpy(blk + firstblock, b_orig(buf), len - firstblock);
+ return len;
+}
+
/* b_getblk() : gets one full block of data at once from a buffer, starting
* from offset <offset> after the buffer's head, and limited to no more than
* <len> bytes. The caller is responsible for ensuring that neither <offset>
@@ -382,6 +435,121 @@ static inline size_t b_getblk_nc(const struct buffer *buf, const char **blk1, si
return 1;
}
+/* Locates the longest part of the buffer that is composed exclusively of
+ * characters not in the <delim> set, and delimited by one of these characters,
+ * and returns the initial part and the first of such delimiters. A single
+ * escape character in <escape> may be specified so that when not 0 and found,
+ * the character that follows it is never taken as a delimiter. Note that
+ * <delim> cannot contain the zero byte, hence this function is not usable with
+ * byte zero as a delimiter.
+ *
+ * Return values :
+ * >0 : number of bytes read. Includes the sep if present before len or end.
+ * =0 : no sep before end found. <str> is left undefined.
+ *
+ * The buffer is left unaffected. Unused buffers are left in an undefined state.
+ */
+static inline size_t b_getdelim(const struct buffer *buf, size_t offset, size_t count,
+ char *str, size_t len, const char *delim, char escape)
+{
+ uchar delim_map[256 / 8];
+ int found, escaped;
+ uint pos, bit;
+ size_t ret, max;
+ uchar b;
+ char *p;
+
+ ret = 0;
+ p = b_peek(buf, offset);
+
+ max = len;
+ if (!count || offset+count > b_data(buf))
+ goto out;
+ if (max > count) {
+ max = count;
+ str[max-1] = 0;
+ }
+
+ /* create the byte map */
+ memset(delim_map, 0, sizeof(delim_map));
+ while ((b = *delim)) {
+ pos = b >> 3;
+ bit = b & 7;
+ delim_map[pos] |= 1 << bit;
+ delim++;
+ }
+
+ found = escaped = 0;
+ while (max) {
+ *str++ = b = *p;
+ ret++;
+ max--;
+
+ if (escape && (escaped || *p == escape)) {
+ escaped = !escaped;
+ goto skip;
+ }
+
+ pos = b >> 3;
+ bit = b & 7;
+ if (delim_map[pos] & (1 << bit)) {
+ found = 1;
+ break;
+ }
+ skip:
+ p = b_next(buf, p);
+ }
+
+ if (ret > 0 && !found)
+ ret = 0;
+ out:
+ if (max)
+ *str = 0;
+ return ret;
+}
+
+/* Gets one text line out of aa buffer.
+ * Return values :
+ * >0 : number of bytes read. Includes the \n if present before len or end.
+ * =0 : no '\n' before end found. <str> is left undefined.
+ *
+ * The buffer is left unaffected. Unused buffers are left in an undefined state.
+ */
+static inline size_t b_getline(const struct buffer *buf, size_t offset, size_t count,
+ char *str, size_t len)
+{
+ size_t ret, max;
+ char *p;
+
+ ret = 0;
+ p = b_peek(buf, offset);
+
+ max = len;
+ if (!count || offset+count > b_data(buf))
+ goto out;
+ if (max > count) {
+ max = count;
+ str[max-1] = 0;
+ }
+
+ while (max) {
+ *str++ = *p;
+ ret++;
+ max--;
+
+ if (*p == '\n')
+ break;
+ p = b_next(buf, p);
+ }
+
+ if (ret > 0 && *(str-1) != '\n')
+ ret = 0;
+ out:
+ if (max)
+ *str = 0;
+ return ret;
+}
+
/*********************************************/
/* Functions used to modify the buffer state */
@@ -536,6 +704,40 @@ static inline void b_putchr(struct buffer *b, char c)
b->data++;
}
+/* b_putblk_ofs(): puts one full block of data of length <len> from <blk> into
+ * the buffer, starting from absolute offset <offset> after the buffer's area.
+ * As a convenience to avoid complex checks in callers, the offset is allowed
+ * to exceed a valid one by no more than one buffer size, and will automatically
+ * be wrapped. The caller is responsible for ensuring that <len> doesn't exceed
+ * the known length of the available room at this position, otherwise data may
+ * be overwritten. The buffer's length is *not* updated, so generally the caller
+ * will have updated it before calling this function. This is meant to be used
+ * on concurrently accessed buffers, so that a writer can append data while a
+ * reader is blocked by other means from reaching the current area The function
+ * guarantees never to use ->head nor ->data. It always returns the number of
+ * bytes copied.
+ */
+static inline size_t b_putblk_ofs(struct buffer *buf, char *blk, size_t len, size_t offset)
+{
+ size_t firstblock;
+
+ if (offset >= buf->size)
+ offset -= buf->size;
+
+ BUG_ON(offset >= buf->size);
+
+ firstblock = buf->size - offset;
+
+ if (firstblock >= len)
+ firstblock = len;
+
+ memcpy(b_orig(buf) + offset, blk, firstblock);
+
+ if (len > firstblock)
+ memcpy(b_orig(buf), blk + firstblock, len - firstblock);
+ return len;
+}
+
/* __b_putblk() : tries to append <len> bytes from block <blk> to the end of
* buffer <b> without checking for free space (it's up to the caller to do it).
* Supports wrapping. It must not be called with len == 0.
@@ -619,7 +821,7 @@ static inline size_t b_xfer(struct buffer *dst, struct buffer *src, size_t count
* b_room(dst).
* Returns the number of bytes copied.
*/
-static inline size_t b_ncat(struct buffer *dst, struct buffer *src, size_t count)
+static inline size_t b_ncat(struct buffer *dst, const struct buffer *src, size_t count)
{
size_t ret, block1, block2;
diff --git a/include/haproxy/bug.h b/include/haproxy/bug.h
index 1356acf..b89ed22 100644
--- a/include/haproxy/bug.h
+++ b/include/haproxy/bug.h
@@ -28,6 +28,7 @@
#ifndef _HAPROXY_BUG_H
#define _HAPROXY_BUG_H
+#include <stddef.h>
#include <haproxy/atomic.h>
#include <haproxy/compiler.h>
@@ -85,6 +86,23 @@ static inline __attribute((always_inline)) void ha_crash_now(void)
#endif // end of arch-specific ha_crash_now() definitions
+
+/* ABORT_NOW() usually takes no argument and will cause the program to abort
+ * exactly where it is. We prefer to emit an invalid instruction to preserve
+ * all registers, but it may fall back to a regular abort depending on the
+ * platform. An optional argument can be a message string that will cause
+ * the emission of a message saying "ABORT at" followed by the file and line
+ * number then that message followed by a final line feed. This can be helpful
+ * in situations where the core cannot be retrieved for example. However it
+ * will definitely cause the loss of some registers, so should be avoided when
+ * not strictly necessary.
+ */
+#define ABORT_NOW(...) \
+ _ABORT_NOW(__FILE__, __LINE__, __VA_ARGS__)
+
+#define _ABORT_NOW(file, line, ...) \
+ __ABORT_NOW(file, line, __VA_ARGS__)
+
#ifdef DEBUG_USE_ABORT
/* abort() is better recognized by code analysis tools */
@@ -104,12 +122,22 @@ static __attribute__((noinline,noreturn,unused)) void abort_with_line(uint line)
abort();
}
-#define ABORT_NOW() do { DUMP_TRACE(); abort_with_line(__LINE__); } while (0)
+#define __ABORT_NOW(file, line, ...) do { \
+ if (sizeof("" __VA_ARGS__) > 1) \
+ complain(NULL, "\nABORT at " file ":" #line ": " __VA_ARGS__ "\n", 1); \
+ DUMP_TRACE(); \
+ abort_with_line(__LINE__); \
+ } while (0)
#else
/* More efficient than abort() because it does not mangle the
* stack and stops at the exact location we need.
*/
-#define ABORT_NOW() do { DUMP_TRACE(); ha_crash_now(); } while (0)
+#define __ABORT_NOW(file, line, ...) do { \
+ if (sizeof("" __VA_ARGS__) > 1) \
+ complain(NULL, "\nABORT at " file ":" #line ": " __VA_ARGS__ "\n", 1); \
+ DUMP_TRACE(); \
+ ha_crash_now(); \
+ } while (0)
#endif
/* This is the generic low-level macro dealing with conditional warnings and
@@ -118,13 +146,21 @@ static __attribute__((noinline,noreturn,unused)) void abort_with_line(uint line)
* the case where it wouldn't die. The <crash> flag is made of:
* - crash & 1: crash yes/no;
* - crash & 2: taint as bug instead of warn
+ * The optional argument must be a single constant string that will be appended
+ * on a second line after the condition message, to give a bit more context
+ * about the problem.
*/
-#define _BUG_ON(cond, file, line, crash, pfx, sfx) \
- __BUG_ON(cond, file, line, crash, pfx, sfx)
+#define _BUG_ON(cond, file, line, crash, pfx, sfx, ...) \
+ __BUG_ON(cond, file, line, crash, pfx, sfx, __VA_ARGS__)
-#define __BUG_ON(cond, file, line, crash, pfx, sfx) \
+#define __BUG_ON(cond, file, line, crash, pfx, sfx, ...) \
(void)(unlikely(cond) ? ({ \
- complain(NULL, "\n" pfx "condition \"" #cond "\" matched at " file ":" #line "" sfx "\n", crash); \
+ const char *msg; \
+ if (sizeof("" __VA_ARGS__) > 1) \
+ msg ="\n" pfx "condition \"" #cond "\" matched at " file ":" #line "" sfx "\n" __VA_ARGS__ "\n"; \
+ else \
+ msg = "\n" pfx "condition \"" #cond "\" matched at " file ":" #line "" sfx "\n"; \
+ complain(NULL, msg, crash); \
if (crash & 1) \
ABORT_NOW(); \
else \
@@ -137,13 +173,18 @@ static __attribute__((noinline,noreturn,unused)) void abort_with_line(uint line)
* certain unexpected conditions in field. Later on, in cores it will be
* possible to verify these counters.
*/
-#define _BUG_ON_ONCE(cond, file, line, crash, pfx, sfx) \
- __BUG_ON_ONCE(cond, file, line, crash, pfx, sfx)
+#define _BUG_ON_ONCE(cond, file, line, crash, pfx, sfx, ...) \
+ __BUG_ON_ONCE(cond, file, line, crash, pfx, sfx, __VA_ARGS__)
-#define __BUG_ON_ONCE(cond, file, line, crash, pfx, sfx) \
+#define __BUG_ON_ONCE(cond, file, line, crash, pfx, sfx, ...) \
(void)(unlikely(cond) ? ({ \
static int __match_count_##line; \
- complain(&__match_count_##line, "\n" pfx "condition \"" #cond "\" matched at " file ":" #line "" sfx "\n", crash); \
+ const char *msg; \
+ if (sizeof("" __VA_ARGS__) > 1) \
+ msg ="\n" pfx "condition \"" #cond "\" matched at " file ":" #line "" sfx "\n" __VA_ARGS__ "\n"; \
+ else \
+ msg = "\n" pfx "condition \"" #cond "\" matched at " file ":" #line "" sfx "\n"; \
+ complain(&__match_count_##line, msg, crash); \
if (crash & 1) \
ABORT_NOW(); \
else \
@@ -163,32 +204,32 @@ static __attribute__((noinline,noreturn,unused)) void abort_with_line(uint line)
*/
/* The macros below are for general use */
-#if defined(DEBUG_STRICT)
+#if defined(DEBUG_STRICT) && (DEBUG_STRICT > 0)
# if defined(DEBUG_STRICT_ACTION) && (DEBUG_STRICT_ACTION < 1)
/* Lowest level: BUG_ON() warns, WARN_ON() warns, CHECK_IF() warns */
-# define BUG_ON(cond) _BUG_ON (cond, __FILE__, __LINE__, 2, "WARNING: bug ", " (not crashing but process is untrusted now, please report to developers)")
-# define WARN_ON(cond) _BUG_ON (cond, __FILE__, __LINE__, 0, "WARNING: warn ", " (please report to developers)")
-# define CHECK_IF(cond) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: check ", " (please report to developers)")
+# define BUG_ON(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 2, "WARNING: bug ", " (not crashing but process is untrusted now, please report to developers)", __VA_ARGS__)
+# define WARN_ON(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 0, "WARNING: warn ", " (please report to developers)", __VA_ARGS__)
+# define CHECK_IF(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: check ", " (please report to developers)", __VA_ARGS__)
# elif !defined(DEBUG_STRICT_ACTION) || (DEBUG_STRICT_ACTION == 1)
/* default level: BUG_ON() crashes, WARN_ON() warns, CHECK_IF() warns */
-# define BUG_ON(cond) _BUG_ON (cond, __FILE__, __LINE__, 3, "FATAL: bug ", "")
-# define WARN_ON(cond) _BUG_ON (cond, __FILE__, __LINE__, 0, "WARNING: warn ", " (please report to developers)")
-# define CHECK_IF(cond) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: check ", " (please report to developers)")
+# define BUG_ON(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 3, "FATAL: bug ", "", __VA_ARGS__)
+# define WARN_ON(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 0, "WARNING: warn ", " (please report to developers)", __VA_ARGS__)
+# define CHECK_IF(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: check ", " (please report to developers)", __VA_ARGS__)
# elif defined(DEBUG_STRICT_ACTION) && (DEBUG_STRICT_ACTION == 2)
/* Stricter level: BUG_ON() crashes, WARN_ON() crashes, CHECK_IF() warns */
-# define BUG_ON(cond) _BUG_ON (cond, __FILE__, __LINE__, 3, "FATAL: bug ", "")
-# define WARN_ON(cond) _BUG_ON (cond, __FILE__, __LINE__, 1, "FATAL: warn ", "")
-# define CHECK_IF(cond) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: check ", " (please report to developers)")
+# define BUG_ON(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 3, "FATAL: bug ", "", __VA_ARGS__)
+# define WARN_ON(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 1, "FATAL: warn ", "", __VA_ARGS__)
+# define CHECK_IF(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: check ", " (please report to developers)", __VA_ARGS__)
# elif defined(DEBUG_STRICT_ACTION) && (DEBUG_STRICT_ACTION >= 3)
/* Developer/CI level: BUG_ON() crashes, WARN_ON() crashes, CHECK_IF() crashes */
-# define BUG_ON(cond) _BUG_ON (cond, __FILE__, __LINE__, 3, "FATAL: bug ", "")
-# define WARN_ON(cond) _BUG_ON (cond, __FILE__, __LINE__, 1, "FATAL: warn ", "")
-# define CHECK_IF(cond) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 1, "FATAL: check ", "")
+# define BUG_ON(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 3, "FATAL: bug ", "", __VA_ARGS__)
+# define WARN_ON(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 1, "FATAL: warn ", "", __VA_ARGS__)
+# define CHECK_IF(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 1, "FATAL: check ", "", __VA_ARGS__)
# endif
#else
-# define BUG_ON(cond) do { (void)sizeof(cond); } while (0)
-# define WARN_ON(cond) do { (void)sizeof(cond); } while (0)
-# define CHECK_IF(cond) do { (void)sizeof(cond); } while (0)
+# define BUG_ON(cond, ...) do { (void)sizeof(cond); } while (0)
+# define WARN_ON(cond, ...) do { (void)sizeof(cond); } while (0)
+# define CHECK_IF(cond, ...) do { (void)sizeof(cond); } while (0)
#endif
/* These macros are only for hot paths and remain disabled unless DEBUG_STRICT is 2 or above.
@@ -198,20 +239,20 @@ static __attribute__((noinline,noreturn,unused)) void abort_with_line(uint line)
#if defined(DEBUG_STRICT) && (DEBUG_STRICT > 1)
# if defined(DEBUG_STRICT_ACTION) && (DEBUG_STRICT_ACTION < 1)
/* Lowest level: BUG_ON() warns, CHECK_IF() warns */
-# define BUG_ON_HOT(cond) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 2, "WARNING: bug ", " (not crashing but process is untrusted now, please report to developers)")
-# define CHECK_IF_HOT(cond) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: check ", " (please report to developers)")
+# define BUG_ON_HOT(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 2, "WARNING: bug ", " (not crashing but process is untrusted now, please report to developers)", __VA_ARGS__)
+# define CHECK_IF_HOT(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: check ", " (please report to developers)", __VA_ARGS__)
# elif !defined(DEBUG_STRICT_ACTION) || (DEBUG_STRICT_ACTION < 3)
/* default level: BUG_ON() crashes, CHECK_IF() warns */
-# define BUG_ON_HOT(cond) _BUG_ON (cond, __FILE__, __LINE__, 3, "FATAL: bug ", "")
-# define CHECK_IF_HOT(cond) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: check ", " (please report to developers)")
+# define BUG_ON_HOT(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 3, "FATAL: bug ", "", __VA_ARGS__)
+# define CHECK_IF_HOT(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 0, "WARNING: check ", " (please report to developers)", __VA_ARGS__)
# elif defined(DEBUG_STRICT_ACTION) && (DEBUG_STRICT_ACTION >= 3)
/* Developer/CI level: BUG_ON() crashes, CHECK_IF() crashes */
-# define BUG_ON_HOT(cond) _BUG_ON (cond, __FILE__, __LINE__, 3, "FATAL: bug ", "")
-# define CHECK_IF_HOT(cond) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 1, "FATAL: check ", "")
+# define BUG_ON_HOT(cond, ...) _BUG_ON (cond, __FILE__, __LINE__, 3, "FATAL: bug ", "", __VA_ARGS__)
+# define CHECK_IF_HOT(cond, ...) _BUG_ON_ONCE(cond, __FILE__, __LINE__, 1, "FATAL: check ", "", __VA_ARGS__)
# endif
#else
-# define BUG_ON_HOT(cond) do { (void)sizeof(cond); } while (0)
-# define CHECK_IF_HOT(cond) do { (void)sizeof(cond); } while (0)
+# define BUG_ON_HOT(cond, ...) do { (void)sizeof(cond) ; } while (0)
+# define CHECK_IF_HOT(cond, ...) do { (void)sizeof(cond) ; } while (0)
#endif
diff --git a/include/haproxy/cbuf-t.h b/include/haproxy/cbuf-t.h
index 27d3bf1..fee97c3 100644
--- a/include/haproxy/cbuf-t.h
+++ b/include/haproxy/cbuf-t.h
@@ -27,6 +27,7 @@
#endif
#endif
+#include <stddef.h>
#include <haproxy/list-t.h>
extern struct pool_head *pool_head_cbuf;
diff --git a/include/haproxy/cfgparse.h b/include/haproxy/cfgparse.h
index adcabb3..3a769d5 100644
--- a/include/haproxy/cfgparse.h
+++ b/include/haproxy/cfgparse.h
@@ -36,6 +36,7 @@ struct acl_cond;
#define CFG_USERLIST 3
#define CFG_PEERS 4
#define CFG_CRTLIST 5
+#define CFG_CRTSTORE 6
/* various keyword modifiers */
enum kw_mod {
diff --git a/include/haproxy/channel.h b/include/haproxy/channel.h
index 17dd75f..22949e1 100644
--- a/include/haproxy/channel.h
+++ b/include/haproxy/channel.h
@@ -818,6 +818,69 @@ static inline size_t channel_empty(const struct channel *chn)
return (IS_HTX_STRM(chn) ? htx_is_empty(htxbuf(&chn->buf)) : c_empty(chn));
}
+/* Check channel's last_read date against the idle timeer to verify the producer
+ * is still streaming data or not
+ */
+static inline void channel_check_idletimer(struct channel *chn)
+{
+ if ((chn->flags & (CF_STREAMER | CF_STREAMER_FAST)) && !co_data(chn) &&
+ global.tune.idle_timer &&
+ (unsigned short)(now_ms - chn->last_read) >= global.tune.idle_timer) {
+ /* The buffer was empty and nothing was transferred for more
+ * than one second. This was caused by a pause and not by
+ * congestion. Reset any streaming mode to reduce latency.
+ */
+ chn->xfer_small = 0;
+ chn->xfer_large = 0;
+ chn->flags &= ~(CF_STREAMER | CF_STREAMER_FAST);
+ }
+}
+
+/* Check amount of transferred data after a receive. If <xferred> is greater
+ * than 0, the <last_read> date is updated and STREAMER flags for the channels
+ * are verified.
+ */
+static inline void channel_check_xfer(struct channel *chn, size_t xferred)
+{
+ if (!xferred)
+ return;
+
+ if ((chn->flags & (CF_STREAMER | CF_STREAMER_FAST)) &&
+ (xferred <= c_size(chn) / 2)) {
+ chn->xfer_large = 0;
+ chn->xfer_small++;
+ if (chn->xfer_small >= 3) {
+ /* we have read less than half of the buffer in
+ * one pass, and this happened at least 3 times.
+ * This is definitely not a streamer.
+ */
+ chn->flags &= ~(CF_STREAMER | CF_STREAMER_FAST);
+ }
+ else if (chn->xfer_small >= 2) {
+ /* if the buffer has been at least half full twchne,
+ * we receive faster than we send, so at least it
+ * is not a "fast streamer".
+ */
+ chn->flags &= ~CF_STREAMER_FAST;
+ }
+ }
+ else if (!(chn->flags & CF_STREAMER_FAST) && (xferred >= channel_data_limit(chn))) {
+ /* we read a full buffer at once */
+ chn->xfer_small = 0;
+ chn->xfer_large++;
+ if (chn->xfer_large >= 3) {
+ /* we call this buffer a fast streamer if it manages
+ * to be filled in one call 3 consecutive times.
+ */
+ chn->flags |= (CF_STREAMER | CF_STREAMER_FAST);
+ }
+ }
+ else {
+ chn->xfer_small = 0;
+ chn->xfer_large = 0;
+ }
+ chn->last_read = now_ms;
+}
/* Returns the amount of bytes that can be written over the input data at once,
* including reserved space which may be overwritten. This is used by Lua to
@@ -852,12 +915,17 @@ static inline int ci_space_for_replace(const struct channel *chn)
*/
static inline int channel_alloc_buffer(struct channel *chn, struct buffer_wait *wait)
{
- if (b_alloc(&chn->buf) != NULL)
- return 1;
+ int force_noqueue;
- if (!LIST_INLIST(&wait->list))
- LIST_APPEND(&th_ctx->buffer_wq, &wait->list);
+ /* If the producer has been notified of recent availability, we must
+ * not check the queue again.
+ */
+ force_noqueue = !!(chn_prod(chn)->flags & SC_FL_HAVE_BUFF);
+
+ if (b_alloc(&chn->buf, DB_CHANNEL | (force_noqueue ? DB_F_NOQUEUE : 0)) != NULL)
+ return 1;
+ b_requeue(DB_CHANNEL, wait);
return 0;
}
diff --git a/include/haproxy/cli-t.h b/include/haproxy/cli-t.h
index cad6728..8555ea8 100644
--- a/include/haproxy/cli-t.h
+++ b/include/haproxy/cli-t.h
@@ -45,7 +45,7 @@
#define APPCTX_CLI_ST1_PAYLOAD (1 << 1)
#define APPCTX_CLI_ST1_NOLF (1 << 2)
#define APPCTX_CLI_ST1_TIMED (1 << 3)
-#define APPCTX_CLI_ST1_SHUT_EXPECTED (1 << 4)
+#define APPCTX_CLI_ST1_LASTCMD (1 << 4)
#define CLI_PREFIX_KW_NB 5
#define CLI_MAX_MATCHES 5
@@ -56,6 +56,7 @@ enum {
CLI_ST_INIT = 0, /* initial state, must leave to zero ! */
CLI_ST_END, /* final state, let's close */
CLI_ST_GETREQ, /* wait for a request */
+ CLI_ST_PARSEREQ, /* parse a request */
CLI_ST_OUTPUT, /* all states after this one are responses */
CLI_ST_PROMPT, /* display the prompt (first output, same code) */
CLI_ST_PRINT, /* display const message in cli->msg */
@@ -82,6 +83,31 @@ struct cli_print_ctx {
int severity; /* severity of the message to be returned according to (syslog) rfc5424 */
};
+/* context for the "wait" command that's used to wait for some time on a
+ * condition. We store the start date and the expiration date. The error
+ * value is set by the I/O handler to be printed by the release handler at
+ * the end.
+ */
+enum cli_wait_err {
+ CLI_WAIT_ERR_DONE, // condition satisfied
+ CLI_WAIT_ERR_INTR, // interrupted
+ CLI_WAIT_ERR_EXP, // finished on wait expiration
+ CLI_WAIT_ERR_FAIL, // finished early (unrecoverable)
+};
+
+enum cli_wait_cond {
+ CLI_WAIT_COND_NONE, // no condition to wait on
+ CLI_WAIT_COND_SRV_UNUSED,// wait for server to become unused
+};
+
+struct cli_wait_ctx {
+ uint start, deadline; // both are in ticks.
+ enum cli_wait_cond cond; // CLI_WAIT_COND_*
+ enum cli_wait_err error; // CLI_WAIT_ERR_*
+ char *args[4]; // up to 4 args taken at parse time, all strduped
+ const char *msg; // static error message for failures if not NULL
+};
+
struct cli_kw {
const char *str_kw[CLI_PREFIX_KW_NB]; /* keywords ended by NULL, limited to CLI_PREFIX_KW_NB
separated keywords combination */
diff --git a/include/haproxy/compat.h b/include/haproxy/compat.h
index 0fe5a0b..3829060 100644
--- a/include/haproxy/compat.h
+++ b/include/haproxy/compat.h
@@ -94,11 +94,19 @@ typedef struct { } empty_t;
#endif
#ifndef MIN
-#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MIN(a, b) ({ \
+ typeof(a) _a = (a); \
+ typeof(a) _b = (b); \
+ ((_a < _b) ? _a : _b); \
+})
#endif
#ifndef MAX
-#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define MAX(a, b) ({ \
+ typeof(a) _a = (a); \
+ typeof(a) _b = (b); \
+ ((_a > _b) ? _a : _b); \
+})
#endif
/* this is for libc5 for example */
diff --git a/include/haproxy/connection-t.h b/include/haproxy/connection-t.h
index 71269c6..83969da 100644
--- a/include/haproxy/connection-t.h
+++ b/include/haproxy/connection-t.h
@@ -37,6 +37,8 @@
#include <haproxy/port_range-t.h>
#include <haproxy/protocol-t.h>
#include <haproxy/show_flags-t.h>
+#include <haproxy/stconn-t.h>
+#include <haproxy/task-t.h>
#include <haproxy/thread-t.h>
/* referenced below */
@@ -54,14 +56,6 @@ struct bind_conf;
struct qcs;
struct ssl_sock_ctx;
-/* Note: subscribing to these events is only valid after the caller has really
- * attempted to perform the operation, and failed to proceed or complete.
- */
-enum sub_event_type {
- SUB_RETRY_RECV = 0x00000001, /* Schedule the tasklet when we can attempt to recv again */
- SUB_RETRY_SEND = 0x00000002, /* Schedule the tasklet when we can attempt to send again */
-};
-
/* For each direction, we have a CO_FL_XPRT_<DIR>_ENA flag, which
* indicates if read or write is desired in that direction for the respective
* layers. The current status corresponding to the current layer being used is
@@ -87,10 +81,11 @@ enum {
CO_FL_REVERSED = 0x00000004, /* connection has been reversed to backend / reversed and accepted on frontend */
CO_FL_ACT_REVERSING = 0x00000008, /* connection has been reversed to frontend but not yet accepted */
- /* unused : 0x00000008 */
- /* unused : 0x00000010 */
- /* unused : 0x00000020 */
+ CO_FL_OPT_MARK = 0x00000010, /* connection has a special sockopt mark */
+
+ CO_FL_OPT_TOS = 0x00000020, /* connection has a special sockopt tos */
+
/* unused : 0x00000040, 0x00000080 */
/* These flags indicate whether the Control and Transport layers are initialized */
@@ -173,13 +168,14 @@ static forceinline char *conn_show_flags(char *buf, size_t len, const char *deli
_(0);
/* flags */
_(CO_FL_SAFE_LIST, _(CO_FL_IDLE_LIST, _(CO_FL_CTRL_READY,
- _(CO_FL_REVERSED, _(CO_FL_ACT_REVERSING, _(CO_FL_XPRT_READY,
- _(CO_FL_WANT_DRAIN, _(CO_FL_WAIT_ROOM, _(CO_FL_EARLY_SSL_HS, _(CO_FL_EARLY_DATA,
- _(CO_FL_SOCKS4_SEND, _(CO_FL_SOCKS4_RECV, _(CO_FL_SOCK_RD_SH, _(CO_FL_SOCK_WR_SH,
- _(CO_FL_ERROR, _(CO_FL_FDLESS, _(CO_FL_WAIT_L4_CONN, _(CO_FL_WAIT_L6_CONN,
- _(CO_FL_SEND_PROXY, _(CO_FL_ACCEPT_PROXY, _(CO_FL_ACCEPT_CIP, _(CO_FL_SSL_WAIT_HS,
- _(CO_FL_PRIVATE, _(CO_FL_RCVD_PROXY, _(CO_FL_SESS_IDLE, _(CO_FL_XPRT_TRACKED
- ))))))))))))))))))))))))));
+ _(CO_FL_REVERSED, _(CO_FL_ACT_REVERSING, _(CO_FL_OPT_MARK, _(CO_FL_OPT_TOS,
+ _(CO_FL_XPRT_READY, _(CO_FL_WANT_DRAIN, _(CO_FL_WAIT_ROOM, _(CO_FL_EARLY_SSL_HS,
+ _(CO_FL_EARLY_DATA, _(CO_FL_SOCKS4_SEND, _(CO_FL_SOCKS4_RECV, _(CO_FL_SOCK_RD_SH,
+ _(CO_FL_SOCK_WR_SH, _(CO_FL_ERROR, _(CO_FL_FDLESS, _(CO_FL_WAIT_L4_CONN,
+ _(CO_FL_WAIT_L6_CONN, _(CO_FL_SEND_PROXY, _(CO_FL_ACCEPT_PROXY, _(CO_FL_ACCEPT_CIP,
+ _(CO_FL_SSL_WAIT_HS, _(CO_FL_PRIVATE, _(CO_FL_RCVD_PROXY, _(CO_FL_SESS_IDLE,
+ _(CO_FL_XPRT_TRACKED
+ ))))))))))))))))))))))))))));
/* epilogue */
_(~0U);
return buf;
@@ -283,18 +279,7 @@ enum {
enum {
CO_SFL_MSG_MORE = 0x0001, /* More data to come afterwards */
CO_SFL_STREAMER = 0x0002, /* Producer is continuously streaming data */
-};
-
-/* mux->shutr() modes */
-enum co_shr_mode {
- CO_SHR_DRAIN = 0, /* read shutdown, drain any extra stuff */
- CO_SHR_RESET = 1, /* read shutdown, reset any extra stuff */
-};
-
-/* mux->shutw() modes */
-enum co_shw_mode {
- CO_SHW_NORMAL = 0, /* regular write shutdown */
- CO_SHW_SILENT = 1, /* imminent close, don't notify peer */
+ CO_SFL_LAST_DATA = 0x0003, /* Sent data are the last ones, shutdown is pending */
};
/* known transport layers (for ease of lookup) */
@@ -338,11 +323,13 @@ enum mux_ctl_type {
MUX_CTL_REVERSE_CONN, /* Notify about an active reverse connection accepted. */
MUX_CTL_SUBS_RECV, /* Notify the mux it must wait for read events again */
MUX_CTL_GET_GLITCHES, /* returns number of glitches on the connection */
+ MUX_CTL_GET_NBSTRM, /* Return the current number of streams on the connection */
+ MUX_CTL_GET_MAXSTRM, /* Return the max number of streams supported by the connection */
};
/* sctl command used by mux->sctl() */
enum mux_sctl_type {
- MUX_SCTL_SID, /* Return the mux stream ID as ouput, as a signed 64bits integer */
+ MUX_SCTL_SID, /* Return the mux stream ID as output, as a signed 64bits integer */
};
/* response for ctl MUX_STATUS */
@@ -369,16 +356,6 @@ struct socks4_request {
char user_id[8]; /* the user ID string, variable length, terminated with a null (0x00); Using "HAProxy\0" */
};
-/* Describes a set of subscriptions. Multiple events may be registered at the
- * same time. The callee should assume everything not pending for completion is
- * implicitly possible. It's illegal to change the tasklet if events are still
- * registered.
- */
-struct wait_event {
- struct tasklet *tasklet;
- int events; /* set of enum sub_event_type above */
-};
-
/* A connection handle is how we differentiate two connections on the lower
* layers. It usually is a file descriptor but can be a connection id. The
* CO_FL_FDLESS flag indicates which one is relevant.
@@ -408,7 +385,7 @@ struct xprt_ops {
int (*prepare_srv)(struct server *srv); /* prepare a server context */
void (*destroy_srv)(struct server *srv); /* destroy a server context */
int (*get_alpn)(const struct connection *conn, void *xprt_ctx, const char **str, int *len); /* get application layer name */
- int (*takeover)(struct connection *conn, void *xprt_ctx, int orig_tid); /* Let the xprt know the fd have been taken over */
+ int (*takeover)(struct connection *conn, void *xprt_ctx, int orig_tid, int release); /* Let the xprt know the fd have been taken over */
void (*set_idle)(struct connection *conn, void *xprt_ctx); /* notify the xprt that the connection becomes idle. implies set_used. */
void (*set_used)(struct connection *conn, void *xprt_ctx); /* notify the xprt that the connection leaves idle. implies set_idle. */
char name[8]; /* transport layer name, zero-terminated */
@@ -436,8 +413,7 @@ struct mux_ops {
size_t (*done_fastfwd)(struct stconn *sc); /* Callback to terminate fast data forwarding */
int (*fastfwd)(struct stconn *sc, unsigned int count, unsigned int flags); /* Callback to init fast data forwarding */
int (*resume_fastfwd)(struct stconn *sc, unsigned int flags); /* Callback to resume fast data forwarding */
- void (*shutr)(struct stconn *sc, enum co_shr_mode); /* shutr function */
- void (*shutw)(struct stconn *sc, enum co_shw_mode); /* shutw function */
+ void (*shut)(struct stconn *sc, enum se_shut_mode, struct se_abort_info *reason); /* shutdown function */
int (*attach)(struct connection *conn, struct sedesc *, struct session *sess); /* attach a stconn to an outgoing connection */
struct stconn *(*get_first_sc)(const struct connection *); /* retrieves any valid stconn from this connection */
@@ -453,7 +429,12 @@ struct mux_ops {
int (*used_streams)(struct connection *conn); /* Returns the number of streams in use on a connection. */
void (*destroy)(void *ctx); /* Let the mux know one of its users left, so it may have to disappear */
int (*ctl)(struct connection *conn, enum mux_ctl_type mux_ctl, void *arg); /* Provides information about the mux connection */
- int (*takeover)(struct connection *conn, int orig_tid); /* Attempts to migrate the connection to the current thread */
+
+ /* Attempts to migrate <conn> from <orig_tid> to the current thread. If
+ * <release> is true, it will be destroyed immediately after by caller.
+ */
+ int (*takeover)(struct connection *conn, int orig_tid, int release);
+
unsigned int flags; /* some flags characterizing the mux's capabilities (MX_FL_*) */
char name[8]; /* mux layer name, zero-terminated */
};
@@ -492,14 +473,15 @@ struct conn_src {
* CAUTION! Always update CONN_HASH_PARAMS_TYPE_COUNT when adding a new entry.
*/
enum conn_hash_params_t {
- CONN_HASH_PARAMS_TYPE_SNI = 0x1,
+ CONN_HASH_PARAMS_TYPE_NAME = 0x1,
CONN_HASH_PARAMS_TYPE_DST_ADDR = 0x2,
CONN_HASH_PARAMS_TYPE_DST_PORT = 0x4,
CONN_HASH_PARAMS_TYPE_SRC_ADDR = 0x8,
CONN_HASH_PARAMS_TYPE_SRC_PORT = 0x10,
CONN_HASH_PARAMS_TYPE_PROXY = 0x20,
+ CONN_HASH_PARAMS_TYPE_MARK_TOS = 0x40,
};
-#define CONN_HASH_PARAMS_TYPE_COUNT 6
+#define CONN_HASH_PARAMS_TYPE_COUNT 7
#define CONN_HASH_PAYLOAD_LEN \
(((sizeof(((struct conn_hash_node *)0)->node.key)) * 8) - CONN_HASH_PARAMS_TYPE_COUNT)
@@ -512,8 +494,9 @@ enum conn_hash_params_t {
* connection hash.
*/
struct conn_hash_params {
- uint64_t sni_prehash;
+ uint64_t name_prehash;
uint64_t proxy_prehash;
+ uint64_t mark_tos_prehash;
void *target;
struct sockaddr_storage *src_addr;
struct sockaddr_storage *dst_addr;
@@ -560,7 +543,7 @@ struct connection {
struct mt_list toremove_list; /* list element when idle connection is ready to be purged */
};
union {
- struct list session_list; /* used by backend conns, list of attached connections to a session */
+ struct list sess_el; /* used by private backend conns, list elem into session */
struct list stopping_list; /* used by frontend conns, attach point in mux stopping list */
};
union conn_handle handle; /* connection handle at the socket layer */
@@ -582,6 +565,8 @@ struct connection {
enum obj_type *target; /* Listener for active reverse, server for passive. */
struct buffer name; /* Only used for passive reverse. Used as SNI when connection added to server idle pool. */
} reverse;
+ uint32_t mark; /* set network mark, if CO_FL_OPT_MARK is set */
+ uint8_t tos; /* set ip tos, if CO_FL_OPT_TOS is set */
};
/* node for backend connection in the idle trees for http-reuse
diff --git a/include/haproxy/connection.h b/include/haproxy/connection.h
index c7d9883..aa61cc7 100644
--- a/include/haproxy/connection.h
+++ b/include/haproxy/connection.h
@@ -26,6 +26,7 @@
#include <haproxy/api.h>
#include <haproxy/buf.h>
+#include <haproxy/sock.h>
#include <haproxy/connection-t.h>
#include <haproxy/stconn-t.h>
#include <haproxy/fd.h>
@@ -52,7 +53,7 @@ extern struct mux_stopping_data mux_stopping_data[MAX_THREADS];
/* receive a PROXY protocol header over a connection */
int conn_recv_proxy(struct connection *conn, int flag);
int conn_send_proxy(struct connection *conn, unsigned int flag);
-int make_proxy_line(char *buf, int buf_len, struct server *srv, struct connection *remote, struct stream *strm);
+int make_proxy_line(char *buf, int buf_len, struct server *srv, struct connection *remote, struct stream *strm, struct session *sess);
struct conn_tlv_list *conn_get_tlv(struct connection *conn, int type);
int conn_append_debug_info(struct buffer *buf, const struct connection *conn, const char *pfx);
@@ -88,6 +89,7 @@ void conn_delete_from_tree(struct connection *conn);
void conn_init(struct connection *conn, void *target);
struct connection *conn_new(void *target);
void conn_free(struct connection *conn);
+void conn_release(struct connection *conn);
struct conn_hash_node *conn_alloc_hash_node(struct connection *conn);
struct sockaddr_storage *sockaddr_alloc(struct sockaddr_storage **sap, const struct sockaddr_storage *orig, socklen_t len);
void sockaddr_free(struct sockaddr_storage **sap);
@@ -95,13 +97,7 @@ void sockaddr_free(struct sockaddr_storage **sap);
/* connection hash stuff */
uint64_t conn_calculate_hash(const struct conn_hash_params *params);
-uint64_t conn_hash_prehash(char *buf, size_t size);
-void conn_hash_update(char *buf, size_t *idx,
- const void *data, size_t size,
- enum conn_hash_params_t *flags,
- enum conn_hash_params_t type);
-uint64_t conn_hash_digest(char *buf, size_t bufsize,
- enum conn_hash_params_t flags);
+uint64_t conn_hash_prehash(const char *buf, size_t size);
int conn_reverse(struct connection *conn);
@@ -426,19 +422,7 @@ static inline void conn_set_tos(const struct connection *conn, int tos)
if (!conn || !conn_ctrl_ready(conn) || (conn->flags & CO_FL_FDLESS))
return;
-#ifdef IP_TOS
- if (conn->src->ss_family == AF_INET)
- setsockopt(conn->handle.fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
-#endif
-#ifdef IPV6_TCLASS
- if (conn->src->ss_family == AF_INET6) {
- if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)conn->src)->sin6_addr))
- /* v4-mapped addresses need IP_TOS */
- setsockopt(conn->handle.fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
- else
- setsockopt(conn->handle.fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos));
- }
-#endif
+ sock_set_tos(conn->handle.fd, conn->src, tos);
}
/* Sets the netfilter mark on the connection's socket. The connection is tested
@@ -449,13 +433,7 @@ static inline void conn_set_mark(const struct connection *conn, int mark)
if (!conn || !conn_ctrl_ready(conn) || (conn->flags & CO_FL_FDLESS))
return;
-#if defined(SO_MARK)
- setsockopt(conn->handle.fd, SOL_SOCKET, SO_MARK, &mark, sizeof(mark));
-#elif defined(SO_USER_COOKIE)
- setsockopt(conn->handle.fd, SOL_SOCKET, SO_USER_COOKIE, &mark, sizeof(mark));
-#elif defined(SO_RTABLE)
- setsockopt(conn->handle.fd, SOL_SOCKET, SO_RTABLE, &mark, sizeof(mark));
-#endif
+ sock_set_mark(conn->handle.fd, conn->ctrl->fam->sock_family, mark);
}
/* Sets adjust the TCP quick-ack feature on the connection's socket. The
diff --git a/include/haproxy/counters-t.h b/include/haproxy/counters-t.h
index 933c228..8539d6c 100644
--- a/include/haproxy/counters-t.h
+++ b/include/haproxy/counters-t.h
@@ -23,6 +23,8 @@
#ifndef _HAPROXY_COUNTERS_T_H
#define _HAPROXY_COUNTERS_T_H
+#include <haproxy/freq_ctr-t.h>
+
/* counters used by listeners and frontends */
struct fe_counters {
unsigned int conn_max; /* max # of active sessions */
@@ -63,15 +65,19 @@ struct fe_counters {
long long cache_hits; /* cache hits */
} http;
} p; /* protocol-specific stats */
+
+ struct freq_ctr sess_per_sec; /* sessions per second on this server */
+ struct freq_ctr req_per_sec; /* HTTP requests per second on the frontend */
+ struct freq_ctr conn_per_sec; /* received connections per second on the frontend */
+
+ unsigned long last_change; /* last time, when the state was changed */
};
/* counters used by servers and backends */
struct be_counters {
unsigned int conn_max; /* max # of active sessions */
- long long cum_conn; /* cumulated number of received connections */
long long cum_sess; /* cumulated number of accepted connections */
long long cum_lbconn; /* cumulated number of sessions processed by load balancing (BE only) */
- unsigned long last_sess; /* last session time */
unsigned int cps_max; /* maximum of new connections received per second */
unsigned int sps_max; /* maximum of new connections accepted per second (sessions) */
@@ -116,6 +122,11 @@ struct be_counters {
long long cache_hits; /* cache hits */
} http;
} p; /* protocol-specific stats */
+
+ struct freq_ctr sess_per_sec; /* sessions per second on this server */
+
+ unsigned long last_sess; /* last session time */
+ unsigned long last_change; /* last time, when the state was changed */
};
#endif /* _HAPROXY_COUNTERS_T_H */
diff --git a/include/haproxy/defaults.h b/include/haproxy/defaults.h
index 7430c61..eda346a 100644
--- a/include/haproxy/defaults.h
+++ b/include/haproxy/defaults.h
@@ -22,6 +22,8 @@
#ifndef _HAPROXY_DEFAULTS_H
#define _HAPROXY_DEFAULTS_H
+#include <haproxy/compat.h>
+
/* MAX_THREADS defines the highest limit for the global nbthread value. It
* defaults to the number of bits in a long integer when threads are enabled
* but may be lowered to save resources on embedded systems.
@@ -69,18 +71,9 @@
#define BUFSIZE 16384
#endif
-/* certain buffers may only be allocated for responses in order to avoid
- * deadlocks caused by request queuing. 2 buffers is the absolute minimum
- * acceptable to ensure that a request gaining access to a server can get
- * a response buffer even if it doesn't completely flush the request buffer.
- * The worst case is an applet making use of a request buffer that cannot
- * completely be sent while the server starts to respond, and all unreserved
- * buffers are allocated by request buffers from pending connections in the
- * queue waiting for this one to flush. Both buffers reserved buffers may
- * thus be used at the same time.
- */
+// number of per-thread emergency buffers for low-memory conditions
#ifndef RESERVED_BUFS
-#define RESERVED_BUFS 2
+#define RESERVED_BUFS 4
#endif
// reserved buffer space for header rewriting
@@ -478,6 +471,10 @@
#define CONFIG_HAP_POOL_BUCKETS (1UL << (CONFIG_HAP_POOL_BUCKETS_BITS))
+#ifndef CONFIG_HAP_TBL_BUCKETS
+# define CONFIG_HAP_TBL_BUCKETS CONFIG_HAP_POOL_BUCKETS
+#endif
+
/* Number of samples used to compute the times reported in stats. A power of
* two is highly recommended, and this value multiplied by the largest response
* time must not overflow and unsigned int. See freq_ctr.h for more information.
@@ -530,4 +527,33 @@
# endif
#endif
+/* number of ring wait queues depending on the number
+ * of threads.
+ */
+#ifndef RING_WAIT_QUEUES
+# if defined(USE_THREAD) && MAX_THREADS >= 32
+# define RING_WAIT_QUEUES 16
+# elif defined(USE_THREAD)
+# define RING_WAIT_QUEUES ((MAX_THREADS + 1) / 2)
+# else
+# define RING_WAIT_QUEUES 1
+# endif
+#endif
+
+/* it has been found that 6 queues was optimal on various archs at various
+ * thread counts, so let's use that by default.
+ */
+#ifndef RING_DFLT_QUEUES
+# define RING_DFLT_QUEUES 6
+#endif
+
+/* Let's make DEBUG_STRICT default to 1 to get rid of it in the makefile */
+#ifndef DEBUG_STRICT
+# define DEBUG_STRICT 1
+#endif
+
+#if !defined(DEBUG_MEMORY_POOLS)
+# define DEBUG_MEMORY_POOLS 1
+#endif
+
#endif /* _HAPROXY_DEFAULTS_H */
diff --git a/include/haproxy/dgram-t.h b/include/haproxy/dgram-t.h
index 4e4c2af..5ed24ef 100644
--- a/include/haproxy/dgram-t.h
+++ b/include/haproxy/dgram-t.h
@@ -22,6 +22,8 @@
#ifndef _HAPROXY_HAPROXY_DGRAM_T_H
#define _HAPROXY_HAPROXY_DGRAM_T_H
+#include <haproxy/api-t.h>
+#include <haproxy/thread-t.h>
#include <arpa/inet.h>
/*
diff --git a/include/haproxy/dns-t.h b/include/haproxy/dns-t.h
index 1c876e3..175c7d1 100644
--- a/include/haproxy/dns-t.h
+++ b/include/haproxy/dns-t.h
@@ -27,8 +27,8 @@
#include <haproxy/connection-t.h>
#include <haproxy/buf-t.h>
#include <haproxy/dgram-t.h>
+#include <haproxy/dns_ring-t.h>
#include <haproxy/obj_type-t.h>
-#include <haproxy/ring-t.h>
#include <haproxy/stats-t.h>
#include <haproxy/task-t.h>
#include <haproxy/thread.h>
@@ -78,7 +78,7 @@ struct dns_additional_record {
*/
struct dns_stream_server {
struct server *srv;
- struct ring *ring_req;
+ struct dns_ring *ring_req;
int max_slots;
int maxconn;
int idle_conns;
@@ -97,7 +97,7 @@ struct dns_stream_server {
struct dns_dgram_server {
struct dgram_conn conn; /* transport layer */
- struct ring *ring_req;
+ struct dns_ring *ring_req;
size_t ofs_req; // ring buffer reader offset
};
@@ -121,7 +121,7 @@ struct dns_session {
struct task *task_exp;
struct eb_root query_ids; /* tree to quickly lookup/retrieve query ids currently in use */
size_t ofs; // ring buffer reader offset
- struct ring ring;
+ struct dns_ring ring;
struct {
uint16_t len;
uint16_t offset;
@@ -136,6 +136,7 @@ struct dns_session {
struct dns_nameserver {
char *id; /* nameserver unique identifier */
void *parent;
+ unsigned int puid; /* parent-unique numeric id */
struct {
const char *file; /* file where the section appears */
int line; /* line where the section appears */
@@ -153,8 +154,9 @@ struct dns_nameserver {
/* mixed dns and resolver counters, we will have to split them */
struct dns_counters {
- char *id;
- char *pid;
+ char *id; /* nameserver id */
+ char *pid; /* parent resolver id */
+ unsigned int ns_puid; /* nameserver numerical id (ns->puid) */
long long sent; /* - queries sent */
long long snd_error; /* - sending errors */
union {
diff --git a/include/haproxy/dns_ring-t.h b/include/haproxy/dns_ring-t.h
new file mode 100644
index 0000000..2c15784
--- /dev/null
+++ b/include/haproxy/dns_ring-t.h
@@ -0,0 +1,110 @@
+/*
+ * include/haproxy/dns_ring-t.h
+ * This file provides definitions for ring buffers used for disposable data.
+ * This is a fork of ring-t.h for DNS usages.
+ *
+ * Copyright (C) 2000-2019 Willy Tarreau - w@1wt.eu
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _HAPROXY_DNS_RING_T_H
+#define _HAPROXY_DNS_RING_T_H
+
+#include <haproxy/api-t.h>
+#include <haproxy/buf-t.h>
+#include <haproxy/thread.h>
+
+/* The code below handles circular buffers with single-producer and multiple
+ * readers (up to 255). The buffer storage area must remain always allocated.
+ * It's made of series of payload blocks followed by a readers count (RC).
+ * There is always a readers count at the beginning of the buffer as well. Each
+ * payload block is composed of a varint-encoded size (VI) followed by the
+ * actual payload (PL).
+ *
+ * The readers count is encoded on a single byte. It indicates how many readers
+ * are still waiting at this position. The writer writes after the buffer's
+ * tail, which initially starts just past the first readers count. Then it
+ * knows by reading this count that it must wake up the readers to indicate
+ * data availability. When a reader reads the payload block, it increments the
+ * next readers count and decrements the current one. The area between the
+ * initial readers count and the next one is protected from overwriting for as
+ * long as the initial count is non-null. As such these readers count are
+ * effective barriers against data recycling.
+ *
+ * Only the writer is allowed to update the buffer's tail/head. This ensures
+ * that events can remain as long as possible so that late readers can get the
+ * maximum history available. It also helps dealing with multi-thread accesses
+ * using a simple RW lock during the buffer head's manipulation. The writer
+ * will have to delete some old records starting at the head until the new
+ * message can fit or a non-null readers count is encountered. If a message
+ * cannot fit due to insufficient room, the message is lost and the drop
+ * counted must be incremented.
+ *
+ * Like any buffer, this buffer naturally wraps at the end and continues at the
+ * beginning. The creation process consists in immediately adding a null
+ * readers count byte into the buffer. The write process consists in always
+ * writing a payload block followed by a new readers count. The delete process
+ * consists in removing a null readers count and payload block. As such, there
+ * is always at least one readers count byte in the buffer available at the
+ * head for new readers to attach to, and one before the tail, both of which
+ * may be the same when the buffer doesn't contain any event. It is thus safe
+ * for any reader to simply keep the absolute offset of the last visited
+ * position and to restart from there. The write will update the buffer's
+ * absolute offset when deleting entries. All this also has the benefit of
+ * allowing a buffer to be hot-resized without losing its contents.
+ *
+ * Thus we have this :
+ * - init of empty buffer:
+ * head-, ,-tail
+ * [ RC | xxxxxxxxxxxxxxxxxxxxxxxxxx ]
+ *
+ * - reader attached:
+ * head-, ,-tail
+ * [ RC | xxxxxxxxxxxxxxxxxxxxxxxxxx ]
+ * ^- +1
+ *
+ * - append of one event:
+ * appended
+ * head-, <----------> ,-tail
+ * [ RC | VI | PL | RC | xxxxxxxxxxx ]
+ *
+ * - reader advancing:
+ * head-, ,-tail
+ * [ RC | VI | PL | RC | xxxxxxxxxxx ]
+ * ^- -1 ^- +1
+ *
+ * - writer removing older message:
+ * head-, ,-tail
+ * [ xxxxxxxxxxxx | RC | xxxxxxxxxxx ]
+ * <---------->
+ * removed
+ */
+
+struct dns_ring {
+ struct buffer buf; // storage area
+ struct mt_list waiters; // list of waiters, for now, CLI "show event"
+ __decl_thread(HA_RWLOCK_T lock);
+ int readers_count;
+};
+
+#endif /* _HAPROXY_DNS_RING_T_H */
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/include/haproxy/dns_ring.h b/include/haproxy/dns_ring.h
new file mode 100644
index 0000000..88bbb4a
--- /dev/null
+++ b/include/haproxy/dns_ring.h
@@ -0,0 +1,46 @@
+/*
+ * include/haproxy/dns_ring.h
+ * Exported functions for ring buffers used for disposable data.
+ * This is a fork of ring.h for DNS usage.
+ *
+ * Copyright (C) 2000-2019 Willy Tarreau - w@1wt.eu
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _HAPROXY_DNS_RING_H
+#define _HAPROXY_DNS_RING_H
+
+#include <stdlib.h>
+#include <import/ist.h>
+#include <haproxy/dns_ring-t.h>
+
+struct appctx;
+
+struct dns_ring *dns_ring_new(size_t size);
+void dns_ring_init(struct dns_ring *ring, void* area, size_t size);
+void dns_ring_free(struct dns_ring *ring);
+ssize_t dns_ring_write(struct dns_ring *ring, size_t maxlen, const struct ist pfx[], size_t npfx, const struct ist msg[], size_t nmsg);
+int dns_ring_attach(struct dns_ring *ring);
+void dns_ring_detach_appctx(struct dns_ring *ring, struct appctx *appctx, size_t ofs);
+
+#endif /* _HAPROXY_DNS_RING_H */
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/include/haproxy/dynbuf-t.h b/include/haproxy/dynbuf-t.h
index b5545ab..f0ca187 100644
--- a/include/haproxy/dynbuf-t.h
+++ b/include/haproxy/dynbuf-t.h
@@ -22,6 +22,79 @@
#ifndef _HAPROXY_DYNBUF_T_H
#define _HAPROXY_DYNBUF_T_H
+#include <haproxy/list-t.h>
+
+/* Describe the levels of criticality of each allocation based on the expected
+ * use case. We distinguish multiple use cases, from the least important to the
+ * most important one:
+ * - allocate a buffer to grow a non-empty ring: this should be avoided when
+ * resources are becoming scarce.
+ * - allocate a buffer for very unlikely situations (e.g. L7 retries, early
+ * data). These may acceptably fail on low resources.
+ * - buffer used to receive data in the mux at the connection level. Please
+ * note that this level might later be resplit into two levels, one for
+ * initial data such as a new request, which may be rejected and postponed,
+ * and one for data continuation, which may be needed to complete a request
+ * or receive some control data allowing another buffer to be flushed.
+ * - buffer used to produce data at the endpoint for internal consumption,
+ * typically mux streams and applets. These buffers will be allocated until
+ * a channel picks them. Not processing them might sometimes lead to a mux
+ * being clogged and blocking other streams from progressing.
+ * - channel buffer: this one may be allocated to perform a synchronous recv,
+ * or just preparing for the possibility of an instant response. The
+ * response channel always allocates a buffer when entering process_stream,
+ * which is immediately released if unused when leaving.
+ * - buffer used by the mux sending side, often allocated by the mux's
+ * snd_buf() handler to encode the outgoing channel's data.
+ * - buffer permanently allocated at boot (e.g. temporary compression
+ * buffers). If these fail, we can't boot.
+ *
+ * Please DO NOT CHANGE THESE LEVELS without first getting a full understanding
+ * of how all this works and touching the DB_F_CRIT_MASK and DB_CRIT_TO_QUEUE()
+ * macros below!
+ */
+enum dynbuf_crit {
+ DB_GROW_RING = 0, // used to grow an existing buffer ring
+ DB_UNLIKELY, // unlikely to be needed (e.g. L7 retries)
+ /* The 4 levels below are subject to queueing */
+ DB_MUX_RX, // buffer used to store incoming data from the system
+ DB_SE_RX, // buffer used to store incoming data for the channel
+ DB_CHANNEL, // buffer used by the channel for synchronous reads
+ DB_MUX_TX, // buffer used to store outgoing mux data
+ /* The one below may never fail */
+ DB_PERMANENT, // buffers permanently allocated.
+};
+
+/* The values above are expected to be passed to b_alloc(). In addition, some
+ * Extra flags can be passed by oring the crit value above with one of these
+ * high-bit flags.
+ */
+#define DB_F_NOQUEUE 0x80000000U // ignore presence of others in queue
+#define DB_F_CRIT_MASK 0x000000FFU // mask to keep the criticality bits
+
+
+/* We'll deal with 4 queues, with indexes numbered from 0 to 3 based on the
+ * criticality of the allocation. All criticality levels are mapped to a 2-bit
+ * queue index. While some levels never use the queue (the first two), some of
+ * the others will share a same queue, and all levels will define a ratio of
+ * allocated emergency buffers below which we refrain from trying to allocate.
+ * In practice, for now the thresholds will just be the queue number times 33%
+ * so that queue 0 is allowed to deplete emergency buffers and queue 3 not at
+ * all. This gives us: queue idx=3 for DB_MUX_RX and below, 2 for DB_SE_RX,
+ * 1 for DB_CHANNEL, 0 for DB_MUX_TX and above. This must match the DYNBUF_NBQ
+ * in tinfo-t.h.
+ */
+
+#define DB_CRIT_TO_QUEUE(crit) ((0x000001BF >> ((crit) * 2)) & 3)
+
+#define DB_GROW_RING_Q DB_CRIT_TO_QUEUE(DB_GROW_RING)
+#define DB_UNLIKELY_Q DB_CRIT_TO_QUEUE(DB_UNLIKELY)
+#define DB_MUX_RX_Q DB_CRIT_TO_QUEUE(DB_MUX_RX)
+#define DB_SE_RX_Q DB_CRIT_TO_QUEUE(DB_SE_RX)
+#define DB_CHANNEL_Q DB_CRIT_TO_QUEUE(DB_CHANNEL)
+#define DB_MUX_TX_Q DB_CRIT_TO_QUEUE(DB_MUX_TX)
+#define DB_PERMANENT_Q DB_CRIT_TO_QUEUE(DB_PERMANENT)
+
/* an element of the <buffer_wq> list. It represents an object that need to
* acquire a buffer to continue its process. */
diff --git a/include/haproxy/dynbuf.h b/include/haproxy/dynbuf.h
index a89800c..4a6595d 100644
--- a/include/haproxy/dynbuf.h
+++ b/include/haproxy/dynbuf.h
@@ -32,6 +32,7 @@
#include <haproxy/buf.h>
#include <haproxy/chunk.h>
#include <haproxy/dynbuf-t.h>
+#include <haproxy/global.h>
#include <haproxy/pool.h>
extern struct pool_head *pool_head_buffer;
@@ -56,21 +57,67 @@ static inline int buffer_almost_full(const struct buffer *buf)
/* Functions below are used for buffer allocation */
/**************************************************/
+/* returns non-zero if one may try to allocate a buffer for criticality flags
+ * <crit> (made of a criticality and optional flags).
+ */
+static inline int b_may_alloc_for_crit(uint crit)
+{
+ int q = DB_CRIT_TO_QUEUE(crit & DB_F_CRIT_MASK);
+
+ /* if this queue or any more critical ones have entries, we must wait */
+ if (!(crit & DB_F_NOQUEUE) && th_ctx->bufq_map & ((2 << q) - 1))
+ return 0;
+
+ /* If the emergency buffers are too low, we won't try to allocate a
+ * buffer either so that we speed up their release. As a corrolary, it
+ * means that we're always allowed to try to fall back to an emergency
+ * buffer if pool_alloc() fails. The minimum number of available
+ * emergency buffers for an allocation depends on the queue:
+ * q == 0 -> 0%
+ * q == 1 -> 33%
+ * q == 2 -> 66%
+ * q == 3 -> 100%
+ */
+ if (th_ctx->emergency_bufs_left * 3 < q * global.tune.reserved_bufs)
+ return 0;
+ return 1;
+}
+
+/* Allocates one of the emergency buffers or returns NULL if there are none left */
+static inline char *__b_get_emergency_buf(void)
+{
+ char *ret;
+
+ if (!th_ctx->emergency_bufs_left)
+ return NULL;
+
+ th_ctx->emergency_bufs_left--;
+ ret = th_ctx->emergency_bufs[th_ctx->emergency_bufs_left];
+ th_ctx->emergency_bufs[th_ctx->emergency_bufs_left] = NULL;
+ return ret;
+}
+
/* Ensures that <buf> is allocated, or allocates it. If no memory is available,
* ((char *)1) is assigned instead with a zero size. The allocated buffer is
* returned, or NULL in case no memory is available. Since buffers only contain
* user data, poisonning is always disabled as it brings no benefit and impacts
* performance. Due to the difficult buffer_wait management, they are not
- * subject to forced allocation failures either.
+ * subject to forced allocation failures either. If other waiters are present
+ * at higher criticality levels, we refrain from allocating.
*/
-#define b_alloc(_buf) \
-({ \
- char *_area; \
- struct buffer *_retbuf = _buf; \
- \
- if (!_retbuf->size) { \
+#define b_alloc(_buf, _crit) \
+({ \
+ char *_area = NULL; \
+ struct buffer *_retbuf = _buf; \
+ uint _criticality = _crit; \
+ \
+ if (!_retbuf->size) { \
*_retbuf = BUF_WANTED; \
- _area = pool_alloc_flag(pool_head_buffer, POOL_F_NO_POISON | POOL_F_NO_FAIL); \
+ if (b_may_alloc_for_crit(_criticality)) { \
+ _area = pool_alloc_flag(pool_head_buffer, POOL_F_NO_POISON | POOL_F_NO_FAIL); \
+ if (unlikely(!_area)) \
+ _area = __b_get_emergency_buf(); \
+ } \
if (unlikely(!_area)) { \
activity[tid].buf_wait++; \
_retbuf = NULL; \
@@ -95,7 +142,10 @@ static inline int buffer_almost_full(const struct buffer *buf)
*/ \
*(_buf) = BUF_NULL; \
__ha_barrier_store(); \
- pool_free(pool_head_buffer, area); \
+ if (th_ctx->emergency_bufs_left < global.tune.reserved_bufs) \
+ th_ctx->emergency_bufs[th_ctx->emergency_bufs_left++] = area; \
+ else \
+ pool_free(pool_head_buffer, area); \
} while (0) \
/* Releases buffer <buf> if allocated, and marks it empty. */
@@ -116,10 +166,90 @@ void __offer_buffers(void *from, unsigned int count);
static inline void offer_buffers(void *from, unsigned int count)
{
- if (!LIST_ISEMPTY(&th_ctx->buffer_wq))
+ int q;
+
+ if (likely(!th_ctx->bufq_map))
+ return;
+
+ for (q = 0; q < DYNBUF_NBQ; q++) {
+ if (!(th_ctx->bufq_map & (1 << q)))
+ continue;
+
+ BUG_ON_HOT(LIST_ISEMPTY(&th_ctx->buffer_wq[q]));
__offer_buffers(from, count);
+ break;
+ }
+}
+
+/* Queues a buffer request for the current thread via <bw>, and returns
+ * non-zero if the criticality allows to queue a request, otherwise returns
+ * zero. If the <bw> was already queued, non-zero is returned so that the call
+ * is idempotent. It is assumed that the buffer_wait struct had already been
+ * preset with its context and callback, otherwise please use b_queue()
+ * instead.
+ */
+static inline int b_requeue(enum dynbuf_crit crit, struct buffer_wait *bw)
+{
+ int q = DB_CRIT_TO_QUEUE(crit);
+
+ if (LIST_INLIST(&bw->list))
+ return 1;
+
+ /* these ones are never queued */
+ if (crit < DB_MUX_RX)
+ return 0;
+
+ th_ctx->bufq_map |= 1 << q;
+ LIST_APPEND(&th_ctx->buffer_wq[q], &bw->list);
+ return 1;
}
+/* Queues a buffer request for the current thread via <bw> with the given <ctx>
+ * and <cb>, and returns non-zero if the criticality allows to queue a request,
+ * otherwise returns zero. If the <bw> was already queued, non-zero is returned
+ * so that the call is idempotent. If the buffer_wait struct had already been
+ * preset with the ctx and cb, please use the lighter b_requeue() instead.
+ */
+static inline int b_queue(enum dynbuf_crit crit, struct buffer_wait *bw, void *ctx, int (*cb)(void *))
+{
+ bw->target = ctx;
+ bw->wakeup_cb = cb;
+ return b_requeue(crit, bw);
+}
+
+/* Dequeues bw element <bw> from its list at for thread <thr> and updates the
+ * thread's bufq_map if it was the last element. The element is assumed to be
+ * in a list (it's the caller's job to test it). This is only meant to really
+ * be used either by the owner thread or under thread isolation. You should
+ * use b_dequeue() instead.
+ */
+static inline void _b_dequeue(struct buffer_wait *bw, int thr)
+{
+ struct thread_ctx *ctx = &ha_thread_ctx[thr];
+ uint q;
+
+ /* trick: detect if we're the last one and pointing to a root, so we
+ * can figure the queue number since the root belongs to an array.
+ */
+ if (LIST_ATMOST1(&bw->list)) {
+ /* OK then which root? */
+ q = bw->list.n - &ctx->buffer_wq[0];
+ BUG_ON_HOT(q >= DYNBUF_NBQ);
+ ctx->bufq_map &= ~(1 << q);
+ }
+ LIST_DEL_INIT(&bw->list);
+}
+
+/* Dequeues bw element <bw> from its list and updates the bufq_map if if was
+ * the last element. All users of buffer_wait should use this to dequeue (e.g.
+ * when killing a pending request on timeout) so as to make sure that we keep
+ * consistency between the list heads and the bitmap.
+ */
+static inline void b_dequeue(struct buffer_wait *bw)
+{
+ if (unlikely(LIST_INLIST(&bw->list)))
+ _b_dequeue(bw, tid);
+}
#endif /* _HAPROXY_DYNBUF_H */
diff --git a/include/haproxy/fcgi-app-t.h b/include/haproxy/fcgi-app-t.h
index fb6ab27..6233aef 100644
--- a/include/haproxy/fcgi-app-t.h
+++ b/include/haproxy/fcgi-app-t.h
@@ -28,6 +28,7 @@
#include <haproxy/acl-t.h>
#include <haproxy/api-t.h>
#include <haproxy/arg-t.h>
+#include <haproxy/log-t.h>
#include <haproxy/fcgi.h>
#include <haproxy/filters-t.h>
#include <haproxy/regex-t.h>
@@ -59,7 +60,7 @@ struct fcgi_rule_conf {
struct fcgi_rule {
enum fcgi_rule_type type;
struct ist name; /* name of the parameter/header */
- struct list value; /* log-format compatible expression, may be empty */
+ struct lf_expr value; /* log-format compatible expression, may be empty */
struct acl_cond *cond; /* acl condition to set the param */
struct list list;
};
@@ -67,7 +68,7 @@ struct fcgi_rule {
/* parameter rule to set/unset a param at the end of the analyzis */
struct fcgi_param_rule {
struct ist name;
- struct list *value; /* if empty , unset the parameter */
+ struct lf_expr *value; /* if empty , unset the parameter */
struct ebpt_node node;
};
diff --git a/include/haproxy/filters-t.h b/include/haproxy/filters-t.h
index c86ef6f..2acacd0 100644
--- a/include/haproxy/filters-t.h
+++ b/include/haproxy/filters-t.h
@@ -22,6 +22,7 @@
#define _HAPROXY_FILTERS_T_H
#include <haproxy/api-t.h>
+#include <haproxy/buf-t.h>
/* Flags set on a filter config */
#define FLT_CFG_FL_HTX 0x00000001 /* The filter can filter HTX streams */
diff --git a/include/haproxy/freq_ctr.h b/include/haproxy/freq_ctr.h
index f3f6903..f037cbb 100644
--- a/include/haproxy/freq_ctr.h
+++ b/include/haproxy/freq_ctr.h
@@ -32,6 +32,14 @@ ullong freq_ctr_total(const struct freq_ctr *ctr, uint period, int pend);
int freq_ctr_overshoot_period(const struct freq_ctr *ctr, uint period, uint freq);
uint update_freq_ctr_period_slow(struct freq_ctr *ctr, uint period, uint inc);
+/* Only usable during single threaded startup phase. */
+static inline void preload_freq_ctr(struct freq_ctr *ctr, uint value)
+{
+ ctr->curr_ctr = 0;
+ ctr->prev_ctr = value;
+ ctr->curr_tick = now_ms & ~1;
+}
+
/* Update a frequency counter by <inc> incremental units. It is automatically
* rotated if the period is over. It is important that it correctly initializes
* a null area.
diff --git a/include/haproxy/global-t.h b/include/haproxy/global-t.h
index 9b3cd78..7665ef2 100644
--- a/include/haproxy/global-t.h
+++ b/include/haproxy/global-t.h
@@ -46,7 +46,7 @@
#define MODE_DUMP_NB_L 0x10000 /* dump line numbers when the configuration file is dump */
/* list of last checks to perform, depending on config options */
-#define LSTCHK_CAP_BIND 0x00000001 /* check that we can bind to any port */
+#define LSTCHK_SYSADM 0x00000001 /* check that we have CAP_SYS_ADMIN */
#define LSTCHK_NETADM 0x00000002 /* check that we have CAP_NET_ADMIN */
/* Global tuning options */
@@ -84,6 +84,7 @@
#define GTUNE_LISTENER_MQ_FAIR (1<<27)
#define GTUNE_LISTENER_MQ_OPT (1<<28)
#define GTUNE_LISTENER_MQ_ANY (GTUNE_LISTENER_MQ_FAIR | GTUNE_LISTENER_MQ_OPT)
+#define GTUNE_QUIC_CC_HYSTART (1<<29)
#define NO_ZERO_COPY_FWD 0x0001 /* Globally disable zero-copy FF */
#define NO_ZERO_COPY_FWD_PT 0x0002 /* disable zero-copy FF for PT (recv & send are disabled automatically) */
@@ -95,6 +96,7 @@
#define NO_ZERO_COPY_FWD_QUIC_SND 0x0080 /* disable zero-copy FF for QUIC on send */
#define NO_ZERO_COPY_FWD_FCGI_RCV 0x0100 /* disable zero-copy FF for FCGI on received */
#define NO_ZERO_COPY_FWD_FCGI_SND 0x0200 /* disable zero-copy FF for FCGI on send */
+#define NO_ZERO_COPY_FWD_APPLET 0x0400 /* disable zero-copy FF for applets */
extern int cluster_secret_isset; /* non zero means a cluster secret was initialized */
@@ -153,6 +155,7 @@ struct global {
char *log_send_hostname; /* set hostname in syslog header */
char *server_state_base; /* path to a directory where server state files can be found */
char *server_state_file; /* path to the file where server states are loaded from */
+ char *stats_file; /* path to stats-file */
unsigned char cluster_secret[16]; /* 128 bits of an SHA1 digest of a secret defined as ASCII string */
struct {
int maxpollevents; /* max number of poll events at once */
@@ -189,9 +192,11 @@ struct global {
int nb_stk_ctr; /* number of stick counters, defaults to MAX_SESS_STKCTR */
int default_shards; /* default shards for listeners, or -1 (by-thread) or -2 (by-group) */
uint max_checks_per_thread; /* if >0, no more than this concurrent checks per thread */
+ uint ring_queues; /* if >0, #ring queues, otherwise equals #thread groups */
#ifdef USE_QUIC
unsigned int quic_backend_max_idle_timeout;
unsigned int quic_frontend_max_idle_timeout;
+ unsigned int quic_frontend_glitches_threshold;
unsigned int quic_frontend_max_streams_bidi;
unsigned int quic_retry_threshold;
unsigned int quic_reorder_ratio;
@@ -209,7 +214,10 @@ struct global {
} unix_bind;
struct proxy *cli_fe; /* the frontend holding the stats settings */
int numa_cpu_mapping;
+ int thread_limit; /* hard limit on the number of threads */
int prealloc_fd;
+ uchar clt_privileged_ports; /* bitmask to allow client privileged ports exchanges per protocol */
+ /* 3-bytes hole */
int cfg_curr_line; /* line number currently being parsed */
const char *cfg_curr_file; /* config file currently being parsed or NULL */
char *cfg_curr_section; /* config section name currently being parsed or NULL */
diff --git a/include/haproxy/global.h b/include/haproxy/global.h
index 2e7fa6b..5553468 100644
--- a/include/haproxy/global.h
+++ b/include/haproxy/global.h
@@ -78,6 +78,7 @@ static inline int already_warned(unsigned int warning)
}
extern unsigned int experimental_directives_allowed;
+extern unsigned int deprecated_directives_allowed;
struct cfg_keyword;
int check_kw_experimental(struct cfg_keyword *kw, const char *file, int linenum,
diff --git a/include/haproxy/guid-t.h b/include/haproxy/guid-t.h
new file mode 100644
index 0000000..9eea355
--- /dev/null
+++ b/include/haproxy/guid-t.h
@@ -0,0 +1,15 @@
+#ifndef _HAPROXY_GUID_T_H
+#define _HAPROXY_GUID_T_H
+
+#include <import/ebtree-t.h>
+#include <haproxy/obj_type-t.h>
+
+/* Maximum GUID size excluding final '\0' */
+#define GUID_MAX_LEN 127
+
+struct guid_node {
+ struct ebpt_node node; /* attach point into GUID global tree */
+ enum obj_type *obj_type; /* pointer to GUID obj owner */
+};
+
+#endif /* _HAPROXY_GUID_T_H */
diff --git a/include/haproxy/guid.h b/include/haproxy/guid.h
new file mode 100644
index 0000000..ecfeb6a
--- /dev/null
+++ b/include/haproxy/guid.h
@@ -0,0 +1,16 @@
+#ifndef _HAPROXY_GUID_H
+#define _HAPROXY_GUID_H
+
+#include <haproxy/guid-t.h>
+
+extern struct eb_root guid_tree;
+
+void guid_init(struct guid_node *node);
+int guid_insert(enum obj_type *obj_type, const char *uid, char **errmsg);
+void guid_remove(struct guid_node *guid);
+struct guid_node *guid_lookup(const char *uid);
+
+int guid_is_valid_fmt(const char *uid, char **errmsg);
+char *guid_name(const struct guid_node *guid);
+
+#endif /* _HAPROXY_GUID_H */
diff --git a/include/haproxy/h1.h b/include/haproxy/h1.h
index 7152c6e..0eb0395 100644
--- a/include/haproxy/h1.h
+++ b/include/haproxy/h1.h
@@ -153,7 +153,6 @@ union h1_sl { /* useful start line pointers, relative t
int h1_headers_to_hdr_list(char *start, const char *stop,
struct http_hdr *hdr, unsigned int hdr_num,
struct h1m *h1m, union h1_sl *slp);
-int h1_measure_trailers(const struct buffer *buf, unsigned int ofs, unsigned int max);
int h1_parse_cont_len_header(struct h1m *h1m, struct ist *value);
int h1_parse_xfer_enc_header(struct h1m *h1m, struct ist value);
diff --git a/include/haproxy/h3.h b/include/haproxy/h3.h
index 1bedf43..8b91061 100644
--- a/include/haproxy/h3.h
+++ b/include/haproxy/h3.h
@@ -48,29 +48,25 @@
#define H3_SETTINGS_MAX_FIELD_SECTION_SIZE 0x06
#define H3_SETTINGS_QPACK_BLOCKED_STREAMS 0x07
-/* Errors. */
+/* RFC 9114 8. Error Handling */
enum h3_err {
- H3_NO_ERROR = 0x100,
- H3_GENERAL_PROTOCOL_ERROR = 0x101,
- H3_INTERNAL_ERROR = 0x102,
- H3_STREAM_CREATION_ERROR = 0x103,
- H3_CLOSED_CRITICAL_STREAM = 0x104,
- H3_FRAME_UNEXPECTED = 0x105,
- H3_FRAME_ERROR = 0x106,
- H3_EXCESSIVE_LOAD = 0x107,
- H3_ID_ERROR = 0x108,
- H3_SETTINGS_ERROR = 0x109,
- H3_MISSING_SETTINGS = 0x10a,
- H3_REQUEST_REJECTED = 0x10b,
- H3_REQUEST_CANCELLED = 0x10c,
- H3_REQUEST_INCOMPLETE = 0x10d,
- H3_MESSAGE_ERROR = 0x10e,
- H3_CONNECT_ERROR = 0x10f,
- H3_VERSION_FALLBACK = 0x110,
-
- QPACK_DECOMPRESSION_FAILED = 0x200,
- QPACK_ENCODER_STREAM_ERROR = 0x201,
- QPACK_DECODER_STREAM_ERROR = 0x202,
+ H3_ERR_NO_ERROR = 0x100,
+ H3_ERR_GENERAL_PROTOCOL_ERROR = 0x101,
+ H3_ERR_INTERNAL_ERROR = 0x102,
+ H3_ERR_STREAM_CREATION_ERROR = 0x103,
+ H3_ERR_CLOSED_CRITICAL_STREAM = 0x104,
+ H3_ERR_FRAME_UNEXPECTED = 0x105,
+ H3_ERR_FRAME_ERROR = 0x106,
+ H3_ERR_EXCESSIVE_LOAD = 0x107,
+ H3_ERR_ID_ERROR = 0x108,
+ H3_ERR_SETTINGS_ERROR = 0x109,
+ H3_ERR_MISSING_SETTINGS = 0x10a,
+ H3_ERR_REQUEST_REJECTED = 0x10b,
+ H3_ERR_REQUEST_CANCELLED = 0x10c,
+ H3_ERR_REQUEST_INCOMPLETE = 0x10d,
+ H3_ERR_MESSAGE_ERROR = 0x10e,
+ H3_ERR_CONNECT_ERROR = 0x10f,
+ H3_ERR_VERSION_FALLBACK = 0x110,
};
/* Frame types. */
diff --git a/include/haproxy/http.h b/include/haproxy/http.h
index 2992640..e28f3cc 100644
--- a/include/haproxy/http.h
+++ b/include/haproxy/http.h
@@ -27,15 +27,20 @@
#include <import/ist.h>
#include <haproxy/api.h>
#include <haproxy/http-t.h>
+#include <haproxy/intops.h>
extern const int http_err_codes[HTTP_ERR_SIZE];
extern const char *http_err_msgs[HTTP_ERR_SIZE];
extern const struct ist http_known_methods[HTTP_METH_OTHER];
extern const uint8_t http_char_classes[256];
+extern long http_err_status_codes[512 / sizeof(long)];
+extern long http_fail_status_codes[512 / sizeof(long)];
enum http_meth_t find_http_meth(const char *str, const int len);
int http_get_status_idx(unsigned int status);
const char *http_get_reason(unsigned int status);
+void http_status_add_range(long *array, uint low, uint high);
+void http_status_del_range(long *array, uint low, uint high);
struct ist http_get_host_port(const struct ist host);
int http_is_default_port(const struct ist schm, const struct ist port);
int http_validate_scheme(const struct ist schm);
@@ -212,6 +217,18 @@ static inline int http_path_has_forbidden_char(const struct ist ist, const char
return 0;
}
+/* Checks status code array <array> for the presence of status code <status>.
+ * Returns non-zero if the code is present, zero otherwise. Any status code is
+ * permitted.
+ */
+static inline int http_status_matches(const long *array, uint status)
+{
+ if (status < 100 || status > 599)
+ return 0;
+
+ return ha_bit_test(status - 100, array);
+}
+
#endif /* _HAPROXY_HTTP_H */
/*
diff --git a/include/haproxy/http_ana-t.h b/include/haproxy/http_ana-t.h
index 5b7342f..f43aa32 100644
--- a/include/haproxy/http_ana-t.h
+++ b/include/haproxy/http_ana-t.h
@@ -73,8 +73,8 @@
/* used only for keep-alive purposes, to indicate we're on a second transaction */
#define TX_NOT_FIRST 0x00040000 /* the transaction is not the first one */
-#define TX_L7_RETRY 0x000800000 /* The transaction may attempt L7 retries */
-#define TX_D_L7_RETRY 0x001000000 /* Disable L7 retries on this transaction, even if configured to do it */
+#define TX_L7_RETRY 0x00080000 /* The transaction may attempt L7 retries */
+#define TX_D_L7_RETRY 0x00100000 /* Disable L7 retries on this transaction, even if configured to do it */
/* This function is used to report flags in debugging tools. Please reflect
* below any single-bit flag addition above in the same order via the
diff --git a/include/haproxy/http_client-t.h b/include/haproxy/http_client-t.h
index 7ae0e61..2c07f77 100644
--- a/include/haproxy/http_client-t.h
+++ b/include/haproxy/http_client-t.h
@@ -64,6 +64,7 @@ enum {
#define HC_F_RES_HDR 0x02
#define HC_F_RES_BODY 0x04
#define HC_F_RES_END 0x08
+#define HC_F_HTTPPROXY 0x10
#endif /* ! _HAPROXY_HTTCLIENT__T_H */
diff --git a/include/haproxy/http_client.h b/include/haproxy/http_client.h
index 241ca24..93f3fc1 100644
--- a/include/haproxy/http_client.h
+++ b/include/haproxy/http_client.h
@@ -1,6 +1,7 @@
#ifndef _HAPROXY_HTTPCLIENT_H
#define _HAPROXY_HTTPCLIENT_H
+#include <haproxy/buf.h>
#include <haproxy/http_client-t.h>
void httpclient_destroy(struct httpclient *hc);
diff --git a/include/haproxy/http_htx-t.h b/include/haproxy/http_htx-t.h
index 8051925..1dd86aa 100644
--- a/include/haproxy/http_htx-t.h
+++ b/include/haproxy/http_htx-t.h
@@ -28,6 +28,7 @@
#include <haproxy/buf-t.h>
#include <haproxy/http-t.h>
+#include <haproxy/log-t.h>
#include <haproxy/htx-t.h>
/* Context used to find/remove an HTTP header. */
@@ -41,9 +42,9 @@ struct http_hdr_ctx {
/* Structure used to build the header list of an HTTP reply */
struct http_reply_hdr {
- struct ist name; /* the header name */
- struct list value; /* the log-format string value */
- struct list list; /* header chained list */
+ struct ist name; /* the header name */
+ struct lf_expr value; /* the log-format string value */
+ struct list list; /* header linked list */
};
#define HTTP_REPLY_EMPTY 0x00 /* the reply has no payload */
@@ -60,7 +61,7 @@ struct http_reply {
char *ctype; /* The response content-type, may be NULL */
struct list hdrs; /* A list of http_reply_hdr */
union {
- struct list fmt; /* A log-format string (type = HTTP_REPLY_LOGFMT) */
+ struct lf_expr fmt; /* A log-format string (type = HTTP_REPLY_LOGFMT) */
struct buffer obj; /* A raw string (type = HTTP_REPLY_RAW) */
struct buffer *errmsg; /* The error message to use as response (type = HTTP_REPLY_ERRMSG).
* may be NULL, if so rely on the proxy error messages */
diff --git a/include/haproxy/htx-t.h b/include/haproxy/htx-t.h
index 2ea6bc8..5312ae1 100644
--- a/include/haproxy/htx-t.h
+++ b/include/haproxy/htx-t.h
@@ -177,7 +177,7 @@ static forceinline char *hsl_show_flags(char *buf, size_t len, const char *delim
#define HTX_FL_PARSING_ERROR 0x00000001 /* Set when a parsing error occurred */
#define HTX_FL_PROCESSING_ERROR 0x00000002 /* Set when a processing error occurred */
#define HTX_FL_FRAGMENTED 0x00000004 /* Set when the HTX buffer is fragmented */
-#define HTX_FL_PROXY_RESP 0x00000008 /* Set when the response was generated by HAProxy */
+/* 0x00000008 unused */
#define HTX_FL_EOM 0x00000010 /* Set when end-of-message is reached from the HTTP point of view
* (at worst, on the EOM block is missing)
*/
@@ -192,7 +192,7 @@ static forceinline char *htx_show_flags(char *buf, size_t len, const char *delim
_(0);
/* flags */
_(HTX_FL_PARSING_ERROR, _(HTX_FL_PROCESSING_ERROR,
- _(HTX_FL_FRAGMENTED, _(HTX_FL_PROXY_RESP, _(HTX_FL_EOM)))));
+ _(HTX_FL_FRAGMENTED, _(HTX_FL_EOM))));
/* epilogue */
_(~0U);
return buf;
@@ -225,7 +225,9 @@ struct htx_ret {
struct htx_blk *blk; /* An HTX block */
};
-/* HTX start-line */
+/* HTX start-line. This is almost always aligned except in rare cases where
+ * parts of the URI are rewritten, hence the packed attribute.
+ */
struct htx_sl {
unsigned int flags; /* HTX_SL_F_* */
union {
@@ -237,11 +239,16 @@ struct htx_sl {
} res;
} info;
- /* XXX 2 bytes unused */
+ /* XXX 2 bytes unused, must be present to keep the rest aligned
+ * (check with "pahole -C htx_sl" that len[] is aligned in case
+ * of doubt).
+ */
+ char __pad_1;
+ char __pad_2;
unsigned int len[3]; /* length of different parts of the start-line */
char l[VAR_ARRAY];
-};
+} __attribute__((packed));
/* Internal representation of an HTTP message */
struct htx {
diff --git a/include/haproxy/intops.h b/include/haproxy/intops.h
index 34010cc..589f90e 100644
--- a/include/haproxy/intops.h
+++ b/include/haproxy/intops.h
@@ -96,6 +96,132 @@ static inline uint64_t rotr64(uint64_t v, uint8_t bits)
return v;
}
+/* Returns non-zero if any of the 4 bytes composing the u32 <x> is below the
+ * value <min8> or above <min8>+127. Please note that the result will be made
+ * of a 0x80 at positions corresponding to the offending bytes, and that as
+ * such the result is a u32 as well. It is designed like this so that the
+ * operation can be cascaded by ORing the results of multiple blocks. It is
+ * crucial for performance that <min8> is passed as a build-time constant so
+ * as to avoid an expensive multiply. A zero on output confirms that all four
+ * bytes are greater than or equal to <min8> and not lower than <min8>-127.
+ * This is essentially used to skip long sequences of text matching the rule
+ * when the cost of stopping on a false positive is low (i.e. parse multiple
+ * bytes at a time and continue one byte at a time at the end of the series).
+ */
+static inline __attribute__((always_inline))
+uint32_t is_char4_below_opt(uint32_t x, uint8_t min8)
+{
+ uint32_t min32 = min8 * 0x01010101U;
+
+ return (x - min32) & 0x80808080U;
+}
+
+/* Returns non-zero if any of the 4 bytes composing the u32 <x> is above the
+ * value <max8> or below <max8>-127. Please note that the result will be made
+ * of a 0x80 at positions corresponding to the offending bytes, and that as
+ * such the result is a u32 as well. It is designed like this so that the
+ * operation can be cascaded by ORing the results of multiple blocks. It is
+ * crucial for performance that <max8> is passed as a build-time constant so
+ * as to avoid an expensive multiply. A zero on output confirms that all four
+ * bytes are lower than or equal to <max8> and not greater than <max8>+127.
+ * This is essentially used to skip long sequences of text matching the rule
+ * when the cost of stopping on a false positive is low (i.e. parse multiple
+ * bytes at a time and continue one byte at a time at the end of the series).
+ */
+static inline __attribute__((always_inline))
+uint32_t is_char4_above_opt(uint32_t x, uint8_t max8)
+{
+ uint32_t max32 = max8 * 0x01010101U;
+
+ return (max32 - x) & 0x80808080U;
+}
+
+/* Returns non-zero if any of the 4 bytes composing the u32 <x> is outside of
+ * the range defined by <min8> to <max8> included. Please note that the result
+ * will be made of a 0x80 at positions corresponding to the offending bytes,
+ * and that as such the result is a u32 as well. It is designed like this so
+ * that the operation can be cascaded by ORing the results of multiple blocks.
+ * There is one restriction in this simplified version, the distance between
+ * min8 and max8 must be lower than 0x80. It is crucial for performance that
+ * the bounds (min8 and max8) are passed as build-time constants so as to avoid
+ * an expensive multiply. A zero on output confirms that all four bytes are
+ * included in the defined range.
+ */
+static inline __attribute__((always_inline))
+uint32_t is_char4_outside(uint32_t x, uint8_t min8, uint8_t max8)
+{
+ uint32_t min32 = min8 * 0x01010101U;
+ uint32_t max32 = max8 * 0x01010101U;
+
+ return (((x - min32) | (max32 - x)) & 0x80808080U);
+}
+
+/* Returns non-zero if any of the 8 bytes composing the u64 <x> is below the
+ * value <min8> or above <min8>+127. Please note that the result will be made
+ * of a 0x80 at positions corresponding to the offending bytes, and that as
+ * such the result is a u64 as well. It is designed like this so that the
+ * operation can be cascaded by ORing the results of multiple blocks. It is
+ * crucial for performance that <min8> is passed as a build-time constant so
+ * as to avoid an expensive multiply. A zero on output confirms that all eight
+ * bytes are greater than or equal to <min8> and not lower than <min8>-127.
+ * This is essentially used to skip long sequences of text matching the rule
+ * when the cost of stopping on a false positive is low (i.e. parse multiple
+ * bytes at a time and continue one byte at a time at the end of the series).
+ */
+static inline __attribute__((always_inline))
+uint64_t is_char8_below_opt(uint64_t x, uint8_t min8)
+{
+ uint64_t min64 = min8 * 0x0101010101010101ULL;
+
+ return (x - min64) & 0x8080808080808080ULL;
+}
+
+/* Returns non-zero if any of the 8 bytes composing the u64 <x> is above the
+ * value <max8> or below <max8>-127. Please note that the result will be made
+ * of a 0x80 at positions corresponding to the offending bytes, and that as
+ * such the result is a u64 as well. It is designed like this so that the
+ * operation can be cascaded by ORing the results of multiple blocks. It is
+ * crucial for performance that <max8> is passed as a build-time constant so
+ * as to avoid an expensive multiply. A zero on output confirms that all eight
+ * bytes are lower than or equal to <max8> and not greater than <max8>+127.
+ * This is essentially used to skip long sequences of text matching the rule
+ * when the cost of stopping on a false positive is low (i.e. parse multiple
+ * bytes at a time and continue one byte at a time at the end of the series).
+ */
+static inline __attribute__((always_inline))
+uint64_t is_char8_above_opt(uint64_t x, uint8_t max8)
+{
+ uint64_t max64 = max8 * 0x0101010101010101ULL;
+
+ return (max64 - x) & 0x8080808080808080ULL;
+}
+
+/* Returns non-zero if any of the 8 bytes composing the u64 <x> is outside of
+ * the range defined by <min8> to <max8> included. Please note that the result
+ * will be made of a 0x80 at positions corresponding to some of the offending
+ * bytes, and that as such the result is a u64 as well. On 32-bit mcahines, the
+ * operation will be made of two adjacent 32-bit checks. It is designed like
+ * this so that the operation can be cascaded by ORing the results of multiple
+ * blocks. There is one restriction in this simplified version, the distance
+ * between min8 and max8 must be lower than 0x80. It is crucial for performance
+ * that the bounds (min8 and max8) are passed as build-time constants so as to
+ * avoid an expensive multiply. A zero on output confirms that all eight bytes
+ * are included in the defined range.
+ */
+static inline __attribute__((always_inline))
+uint64_t is_char8_outside(uint64_t x, uint8_t min8, uint8_t max8)
+{
+ if (sizeof(long) >= 8) {
+ uint64_t min64 = min8 * 0x0101010101010101ULL;
+ uint64_t max64 = max8 * 0x0101010101010101ULL;
+
+ return (((x - min64) | (max64 - x)) & 0x8080808080808080ULL);
+ }
+ else
+ return is_char4_outside(x >> 0, min8, max8) |
+ is_char4_outside(x >> 32, min8, max8);
+}
+
/* Simple popcountl implementation. It returns the number of ones in a word.
* Described here : https://graphics.stanford.edu/~seander/bithacks.html
*/
diff --git a/include/haproxy/jwt-t.h b/include/haproxy/jwt-t.h
index e94607e..d4f9e69 100644
--- a/include/haproxy/jwt-t.h
+++ b/include/haproxy/jwt-t.h
@@ -22,6 +22,7 @@
#ifndef _HAPROXY_JWT_T_H
#define _HAPROXY_JWT_T_H
+#include <import/ebmbtree.h>
#include <haproxy/openssl-compat.h>
#ifdef USE_OPENSSL
diff --git a/include/haproxy/lb_ss-t.h b/include/haproxy/lb_ss-t.h
new file mode 100644
index 0000000..9014bce
--- /dev/null
+++ b/include/haproxy/lb_ss-t.h
@@ -0,0 +1,32 @@
+/*
+ * include/haproxy/lb_ss-t.h
+ * Types for sticky load-balancing
+ *
+ * Copyright 2024 HAProxy Technologies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _HAPROXY_LB_LH_T_H
+#define _HAPROXY_LB_LH_T_H
+
+#include <haproxy/api-t.h>
+#include <haproxy/server-t.h>
+
+struct lb_ss {
+ struct server *srv; /* sticked server */
+};
+
+#endif /* _HAPROXY_LB_LH_T_H */
diff --git a/include/haproxy/lb_ss.h b/include/haproxy/lb_ss.h
new file mode 100644
index 0000000..6ec3153
--- /dev/null
+++ b/include/haproxy/lb_ss.h
@@ -0,0 +1,33 @@
+/*
+ * include/haproxy/lb_ss.h
+ * sticky load-balancing
+ *
+ * Copyright 2024 HAProxy Technologies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _HAPROXY_LB_SS_H
+#define _HAPROXY_LB_SS_H
+
+#include <haproxy/api.h>
+#include <haproxy/proxy-t.h>
+#include <haproxy/server-t.h>
+
+void recalc_server_ss(struct proxy *px);
+void init_server_ss(struct proxy *px);
+struct server *ss_get_server(struct proxy *px);
+
+#endif /* _HAPROXY_LB_SS_H */
diff --git a/include/haproxy/linuxcap.h b/include/haproxy/linuxcap.h
index 9c337a4..9395b7b 100644
--- a/include/haproxy/linuxcap.h
+++ b/include/haproxy/linuxcap.h
@@ -3,5 +3,6 @@
int prepare_caps_for_setuid(int from_uid, int to_uid);
int finalize_caps_after_setuid(int from_uid, int to_uid);
+int prepare_caps_from_permitted_set(int from_uid, int to_uid, const char *program_name);
#endif /* _HAPROXY_LINUXCAP_H */
diff --git a/include/haproxy/list.h b/include/haproxy/list.h
index 368e6d7..b922bc1 100644
--- a/include/haproxy/list.h
+++ b/include/haproxy/list.h
@@ -106,6 +106,13 @@
*/
#define LIST_INLIST(el) ((el)->n != (el))
+/* checks if the list element <el> has the same prev and next, i.e. it's either
+ * detached or alone in a list since (it points to itself or to a single other
+ * node). One can check that an element is strictly attached and alone by
+ * combining this with LIST_INLIST().
+ */
+#define LIST_ATMOST1(el) ((el)->n == (el)->p)
+
/* atomically checks if the list element's next pointer points to anything
* different from itself, implying the element should be part of a list. This
* usually is similar to LIST_INLIST() except that while that one might be
diff --git a/include/haproxy/listener-t.h b/include/haproxy/listener-t.h
index 7f5e52a..b9a8447 100644
--- a/include/haproxy/listener-t.h
+++ b/include/haproxy/listener-t.h
@@ -28,6 +28,7 @@
#include <import/ebtree-t.h>
#include <haproxy/api-t.h>
+#include <haproxy/guid-t.h>
#include <haproxy/obj_type-t.h>
#include <haproxy/quic_cc-t.h>
#include <haproxy/quic_sock-t.h>
@@ -138,7 +139,6 @@ struct ssl_bind_conf {
unsigned int verify:3; /* verify method (set of SSL_VERIFY_* flags) */
unsigned int no_ca_names:1;/* do not send ca names to clients (ca_file related) */
unsigned int early_data:1; /* early data allowed */
- unsigned int ocsp_update:2;/* enable OCSP auto update */
char *ca_file; /* CAfile to use on verify and ca-names */
char *ca_verify_file; /* CAverify file to use on verify only */
char *crl_file; /* CRLfile to use on verify */
@@ -169,9 +169,6 @@ struct bind_conf {
unsigned long long ca_ignerr_bitfield[IGNERR_BF_SIZE]; /* ignored verify errors in handshake if depth > 0 */
unsigned long long crt_ignerr_bitfield[IGNERR_BF_SIZE]; /* ignored verify errors in handshake if depth == 0 */
void *initial_ctx; /* SSL context for initial negotiation */
- void *default_ctx; /* SSL context of first/default certificate */
- struct ckch_inst *default_inst;
- struct ssl_bind_conf *default_ssl_conf; /* custom SSL conf of default_ctx */
int strict_sni; /* refuse negotiation if sni doesn't match a certificate */
int ssl_options; /* ssl options */
struct eb_root sni_ctx; /* sni_ctx tree of all known certs full-names sorted by name */
@@ -210,6 +207,8 @@ struct bind_conf {
char *arg; /* argument passed to "bind" for better error reporting */
char *file; /* file where the section appears */
int line; /* line where the section appears */
+ char *guid_prefix; /* prefix for listeners GUID */
+ size_t guid_idx; /* next index for listeners GUID generation */
char *rhttp_srvname; /* name of server when using "rhttp@" address */
int rhttp_nbconn; /* count of connections to initiate in parallel */
__decl_thread(HA_RWLOCK_T sni_lock); /* lock the SNI trees during add/del operations */
@@ -255,6 +254,8 @@ struct listener {
struct eb32_node id; /* place in the tree of used IDs */
} conf; /* config information */
+ struct guid_node guid; /* GUID global tree node */
+
struct li_per_thread *per_thr; /* per-thread fields (one per thread in the group) */
EXTRA_COUNTERS(extra_counters);
diff --git a/include/haproxy/listener.h b/include/haproxy/listener.h
index 5b3dc18..3627a79 100644
--- a/include/haproxy/listener.h
+++ b/include/haproxy/listener.h
@@ -192,6 +192,13 @@ int default_resume_listener(struct listener *l);
*/
int bind_complete_thread_setup(struct bind_conf *bind_conf, int *err_code);
+/* Generate and insert unique GUID for each listeners of <bind_conf> instance
+ * if GUID prefix is defined.
+ *
+ * Returns 0 on success else non-zero.
+ */
+int bind_generate_guid(struct bind_conf *bind_conf);
+
/*
* Registers the bind keyword list <kwl> as a list of valid keywords for next
* parsing sessions.
diff --git a/include/haproxy/log-t.h b/include/haproxy/log-t.h
index a0a25ac..8768e10 100644
--- a/include/haproxy/log-t.h
+++ b/include/haproxy/log-t.h
@@ -38,6 +38,7 @@
#define UNIQUEID_LEN 128
/* flags used in logformat_node->options */
+#define LOG_OPT_NONE 0x00000000
#define LOG_OPT_HEXA 0x00000001
#define LOG_OPT_MANDATORY 0x00000002
#define LOG_OPT_QUOTE 0x00000004
@@ -46,6 +47,11 @@
#define LOG_OPT_HTTP 0x00000020
#define LOG_OPT_ESC 0x00000040
#define LOG_OPT_MERGE_SPACES 0x00000080
+#define LOG_OPT_BIN 0x00000100
+/* unused: 0x00000200 ... 0x00000800 */
+#define LOG_OPT_ENCODE_JSON 0x00001000
+#define LOG_OPT_ENCODE_CBOR 0x00002000
+#define LOG_OPT_ENCODE 0x00003000
/* Fields that need to be extracted from the incoming connection or request for
@@ -122,75 +128,10 @@ enum log_tgt {
/* lists of fields that can be logged, for logformat_node->type */
enum {
- LOG_FMT_TEXT = 0, /* raw text */
- LOG_FMT_EXPR, /* sample expression */
+ LOG_FMT_TEXT = 0, /* raw text */
+ LOG_FMT_EXPR, /* sample expression */
LOG_FMT_SEPARATOR, /* separator replaced by one space */
-
- /* information fields */
- LOG_FMT_GLOBAL,
- LOG_FMT_CLIENTIP,
- LOG_FMT_CLIENTPORT,
- LOG_FMT_BACKENDIP,
- LOG_FMT_BACKENDPORT,
- LOG_FMT_FRONTENDIP,
- LOG_FMT_FRONTENDPORT,
- LOG_FMT_SERVERPORT,
- LOG_FMT_SERVERIP,
- LOG_FMT_COUNTER,
- LOG_FMT_LOGCNT,
- LOG_FMT_PID,
- LOG_FMT_DATE,
- LOG_FMT_DATEGMT,
- LOG_FMT_DATELOCAL,
- LOG_FMT_TS,
- LOG_FMT_MS,
- LOG_FMT_FRONTEND,
- LOG_FMT_FRONTEND_XPRT,
- LOG_FMT_BACKEND,
- LOG_FMT_SERVER,
- LOG_FMT_BYTES,
- LOG_FMT_BYTES_UP,
- LOG_FMT_Ta,
- LOG_FMT_Th,
- LOG_FMT_Ti,
- LOG_FMT_TQ,
- LOG_FMT_TW,
- LOG_FMT_TC,
- LOG_FMT_Tr,
- LOG_FMT_tr,
- LOG_FMT_trg,
- LOG_FMT_trl,
- LOG_FMT_TR,
- LOG_FMT_TD,
- LOG_FMT_TT,
- LOG_FMT_TU,
- LOG_FMT_STATUS,
- LOG_FMT_CCLIENT,
- LOG_FMT_CSERVER,
- LOG_FMT_TERMSTATE,
- LOG_FMT_TERMSTATE_CK,
- LOG_FMT_ACTCONN,
- LOG_FMT_FECONN,
- LOG_FMT_BECONN,
- LOG_FMT_SRVCONN,
- LOG_FMT_RETRIES,
- LOG_FMT_SRVQUEUE,
- LOG_FMT_BCKQUEUE,
- LOG_FMT_HDRREQUEST,
- LOG_FMT_HDRRESPONS,
- LOG_FMT_HDRREQUESTLIST,
- LOG_FMT_HDRRESPONSLIST,
- LOG_FMT_REQ,
- LOG_FMT_HTTP_METHOD,
- LOG_FMT_HTTP_URI,
- LOG_FMT_HTTP_PATH,
- LOG_FMT_HTTP_PATH_ONLY,
- LOG_FMT_HTTP_QUERY,
- LOG_FMT_HTTP_VERSION,
- LOG_FMT_HOSTNAME,
- LOG_FMT_UNIQUEID,
- LOG_FMT_SSL_CIPHER,
- LOG_FMT_SSL_VERSION,
+ LOG_FMT_ALIAS, /* reference to logformat_alias */
};
/* enum for parse_logformat_string */
@@ -198,8 +139,11 @@ enum {
LF_INIT = 0, // before first character
LF_TEXT, // normal text
LF_SEPARATOR, // a single separator
- LF_VAR, // variable name, after '%' or '%{..}'
- LF_STARTVAR, // % in text
+ LF_ALIAS, // alias name, after '%' or '%{..}'
+ LF_STARTALIAS, // % in text
+ LF_STONAME, // after '%(' and before ')'
+ LF_STOTYPE, // after ':' while in STONAME
+ LF_EDONAME, // ')' after '%('
LF_STARG, // after '%{' and berore '}'
LF_EDARG, // '}' after '%{'
LF_STEXPR, // after '%[' or '%{..}[' and berore ']'
@@ -207,13 +151,49 @@ enum {
LF_END, // \0 found
};
+/* log_format aliases (ie: %alias), see logformat_aliases table in log.c for
+ * available aliases definitions
+ */
+struct logformat_node; // forward-declaration
+struct logformat_alias {
+ char *name;
+ int type;
+ int mode;
+ int lw; /* logwait bitsfield */
+ int (*config_callback)(struct logformat_node *node, struct proxy *curproxy);
+};
struct logformat_node {
struct list list;
int type; // LOG_FMT_*
int options; // LOG_OPT_*
+ int typecast; // explicit typecasting for printing purposes (SMP_T_{SAME,BOOL,STR,SINT})
+ char *name; // printable name for output types that require named fields (ie: json)
char *arg; // text for LOG_FMT_TEXT, arg for others
void *expr; // for use with LOG_FMT_EXPR
+ const struct logformat_alias *alias; // set if ->type == LOG_FMT_ALIAS
+};
+
+enum lf_expr_flags {
+ LF_FL_NONE = 0x00,
+ LF_FL_COMPILED = 0x01
+};
+
+/* a full logformat expr made of one or multiple logformat nodes */
+struct lf_expr {
+ struct list list; /* to store lf_expr inside a list */
+ union {
+ struct {
+ struct list list; /* logformat_node list */
+ int options; /* global '%o' options (common to all nodes) */
+ } nodes;
+ char *str; /* original string prior to parsing (NULL once compiled) */
+ };
+ struct {
+ char *file; /* file where the lft appears */
+ int line; /* line where the lft appears */
+ } conf; // parsing hints
+ uint8_t flags; /* LF_FL_* flags */
};
/* Range of indexes for log sampling. */
diff --git a/include/haproxy/log.h b/include/haproxy/log.h
index 68b8207..bc86552 100644
--- a/include/haproxy/log.h
+++ b/include/haproxy/log.h
@@ -64,8 +64,23 @@ void syslog_fd_handler(int fd);
int init_log_buffers(void);
void deinit_log_buffers(void);
+void lf_expr_init(struct lf_expr *expr);
+int lf_expr_dup(const struct lf_expr *orig, struct lf_expr *dest);
+void lf_expr_xfer(struct lf_expr *src, struct lf_expr *dst);
+void lf_expr_deinit(struct lf_expr *expr);
+static inline int lf_expr_isempty(const struct lf_expr *expr)
+{
+ return !(expr->flags & LF_FL_COMPILED) || LIST_ISEMPTY(&expr->nodes.list);
+}
+int lf_expr_compile(struct lf_expr *lf_expr, struct arg_list *al, int options, int cap, char **err);
+int lf_expr_postcheck(struct lf_expr *lf_expr, struct proxy *px, char **err);
+
+/* Deinitialize log buffers used for syslog messages */
+void free_logformat_list(struct list *fmt);
+void free_logformat_node(struct logformat_node *node);
+
/* build a log line for the session and an optional stream */
-int sess_build_logline(struct session *sess, struct stream *s, char *dst, size_t maxsize, struct list *list_format);
+int sess_build_logline(struct session *sess, struct stream *s, char *dst, size_t maxsize, struct lf_expr *lf_expr);
/*
* send a log for the stream when we have enough info about it.
@@ -81,14 +96,15 @@ void app_log(struct list *loggers, struct buffer *tag, int level, const char *fo
/*
* add to the logformat linked list
*/
-int add_to_logformat_list(char *start, char *end, int type, struct list *list_format, char **err);
+int add_to_logformat_list(char *start, char *end, int type, struct lf_expr *lf_expr, char **err);
+
+ssize_t syslog_applet_append_event(void *ctx, struct ist v1, struct ist v2, size_t ofs, size_t len);
/*
* Parse the log_format string and fill a linked list.
- * Variable name are preceded by % and composed by characters [a-zA-Z0-9]* : %varname
- * You can set arguments using { } : %{many arguments}varname
+ * Refer to source file for details
*/
-int parse_logformat_string(const char *str, struct proxy *curproxy, struct list *list_format, int options, int cap, char **err);
+int parse_logformat_string(const char *str, struct proxy *curproxy, struct lf_expr *lf_expr, int options, int cap, char **err);
int postresolve_logger_list(struct list *loggers, const char *section, const char *section_name);
@@ -131,27 +147,6 @@ int get_log_level(const char *lev);
int get_log_facility(const char *fac);
/*
- * Write a string in the log string
- * Take cares of quote options
- *
- * Return the address of the \0 character, or NULL on error
- */
-char *lf_text_len(char *dst, const char *src, size_t len, size_t size, const struct logformat_node *node);
-
-/*
- * Write a IP address to the log string
- * +X option write in hexadecimal notation, most significant byte on the left
- */
-char *lf_ip(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node);
-
-/*
- * Write a port to the log
- * +X option write in hexadecimal notation, most significant byte on the left
- */
-char *lf_port(char *dst, const struct sockaddr *sockaddr, size_t size, const struct logformat_node *node);
-
-
-/*
* Function to handle log header building (exported for sinks)
*/
char *update_log_hdr_rfc5424(const time_t time, suseconds_t frac);
@@ -162,9 +157,9 @@ char * get_format_pid_sep2(int format, size_t *len);
/*
* Builds a log line for the stream (must be valid).
*/
-static inline int build_logline(struct stream *s, char *dst, size_t maxsize, struct list *list_format)
+static inline int build_logline(struct stream *s, char *dst, size_t maxsize, struct lf_expr *lf_expr)
{
- return sess_build_logline(strm_sess(s), s, dst, maxsize, list_format);
+ return sess_build_logline(strm_sess(s), s, dst, maxsize, lf_expr);
}
struct ist *build_log_header(struct log_header hdr, size_t *nbelem);
diff --git a/include/haproxy/mqtt-t.h b/include/haproxy/mqtt-t.h
index 51f55ea..3af2d11 100644
--- a/include/haproxy/mqtt-t.h
+++ b/include/haproxy/mqtt-t.h
@@ -22,6 +22,7 @@
#ifndef _HAPROXY_MQTT_T_H
#define _HAPROXY_MQTT_T_H
+#include <inttypes.h>
#include <import/ist.h>
/* MQTT protocol version
diff --git a/include/haproxy/mux_h1-t.h b/include/haproxy/mux_h1-t.h
index 2f49a49..e0c29c2 100644
--- a/include/haproxy/mux_h1-t.h
+++ b/include/haproxy/mux_h1-t.h
@@ -31,13 +31,14 @@
/* Flags indicating why writing output data are blocked */
#define H1C_F_OUT_ALLOC 0x00000001 /* mux is blocked on lack of output buffer */
#define H1C_F_OUT_FULL 0x00000002 /* mux is blocked on output buffer full */
-/* 0x00000004 - 0x00000008 unused */
+#define H1C_F_OUT_MAYALLOC 0x00000004 /* mux was just unblocked and may try to alloc out again */
/* Flags indicating why reading input data are blocked. */
+#define H1C_F_IN_MAYALLOC 0x00000008 /* mux was just unblocked and may try to alloc in again */
#define H1C_F_IN_ALLOC 0x00000010 /* mux is blocked on lack of input buffer */
#define H1C_F_IN_FULL 0x00000020 /* mux is blocked on input buffer full */
#define H1C_F_IN_SALLOC 0x00000040 /* mux is blocked on lack of stream's request buffer */
-/* 0x00000080 unused */
+#define H1C_F_IN_SMAYALLOC 0x00000080 /* mux was just unblocked and may try to alloc strm again */
#define H1C_F_EOS 0x00000100 /* End-of-stream seen on the H1 connection (read0 detected) */
#define H1C_F_ERR_PENDING 0x00000200 /* A write error was detected (block sends but not reads) */
@@ -66,12 +67,12 @@ static forceinline char *h1c_show_flags(char *buf, size_t len, const char *delim
/* prologue */
_(0);
/* flags */
- _(H1C_F_OUT_ALLOC, _(H1C_F_OUT_FULL,
- _(H1C_F_IN_ALLOC, _(H1C_F_IN_FULL, _(H1C_F_IN_SALLOC,
+ _(H1C_F_OUT_ALLOC, _(H1C_F_OUT_FULL, _(H1C_F_OUT_MAYALLOC,
+ _(H1C_F_IN_MAYALLOC, _(H1C_F_IN_ALLOC, _(H1C_F_IN_FULL, _(H1C_F_IN_SALLOC, _(H1C_F_IN_SMAYALLOC,
_(H1C_F_EOS, _(H1C_F_ERR_PENDING, _(H1C_F_ERROR,
_(H1C_F_SILENT_SHUT, _(H1C_F_ABRT_PENDING, _(H1C_F_ABRTED,
_(H1C_F_WANT_FASTFWD, _(H1C_F_WAIT_NEXT_REQ, _(H1C_F_UPG_H2C, _(H1C_F_CO_MSG_MORE,
- _(H1C_F_CO_STREAMER, _(H1C_F_CANT_FASTFWD, _(H1C_F_IS_BACK))))))))))))))))));
+ _(H1C_F_CO_STREAMER, _(H1C_F_CANT_FASTFWD, _(H1C_F_IS_BACK)))))))))))))))))))));
/* epilogue */
_(~0U);
return buf;
@@ -92,13 +93,12 @@ static forceinline char *h1c_show_flags(char *buf, size_t len, const char *delim
#define H1S_F_WANT_CLO 0x00000040
#define H1S_F_WANT_MSK 0x00000070
#define H1S_F_NOT_FIRST 0x00000080 /* The H1 stream is not the first one */
-#define H1S_F_BODYLESS_RESP 0x00000100 /* Bodyless response message */
+/* 0x00000100 unused */
#define H1S_F_INTERNAL_ERROR 0x00000200 /* Set when an internal error occurred during the message parsing */
#define H1S_F_NOT_IMPL_ERROR 0x00000400 /* Set when a feature is not implemented during the message parsing */
#define H1S_F_PARSING_ERROR 0x00000800 /* Set when an error occurred during the message parsing */
#define H1S_F_PROCESSING_ERROR 0x00001000 /* Set when an error occurred during the message xfer */
-#define H1S_F_ERROR_MASK 0x00003800 /* stream error mask */
#define H1S_F_HAVE_SRV_NAME 0x00002000 /* Set during output process if the server name header was added to the request */
#define H1S_F_HAVE_O_CONN 0x00004000 /* Set during output process to know connection mode was processed */
@@ -106,6 +106,9 @@ static forceinline char *h1c_show_flags(char *buf, size_t len, const char *delim
#define H1S_F_HAVE_CLEN 0x00010000 /* Set during output process to know C*L header was found or generated */
#define H1S_F_HAVE_CHNK 0x00020000 /* Set during output process to know "T-E; chunk" header was found or generated */
+#define H1S_F_BODYLESS_REQ 0x00040000 /* Bodyless request message */
+#define H1S_F_BODYLESS_RESP 0x00080000 /* Bodyless response message */
+
/* This function is used to report flags in debugging tools. Please reflect
* below any single-bit flag addition above in the same order via the
* __APPEND_FLAG macro. The new end of the buffer is returned.
@@ -118,10 +121,10 @@ static forceinline char *h1s_show_flags(char *buf, size_t len, const char *delim
/* flags */
_(H1S_F_RX_BLK, _(H1S_F_TX_BLK, _(H1S_F_RX_CONGESTED,
_(H1S_F_WANT_KAL, _(H1S_F_WANT_TUN, _(H1S_F_WANT_CLO,
- _(H1S_F_NOT_FIRST, _(H1S_F_BODYLESS_RESP,
+ _(H1S_F_NOT_FIRST,
_(H1S_F_INTERNAL_ERROR, _(H1S_F_NOT_IMPL_ERROR, _(H1S_F_PARSING_ERROR, _(H1S_F_PROCESSING_ERROR,
_(H1S_F_HAVE_SRV_NAME, _(H1S_F_HAVE_O_CONN, _(H1S_F_HAVE_WS_KEY,
- _(H1S_F_HAVE_CLEN, _(H1S_F_HAVE_CHNK)))))))))))))))));
+ _(H1S_F_HAVE_CLEN, _(H1S_F_HAVE_CHNK, _(H1S_F_BODYLESS_REQ, _(H1S_F_BODYLESS_RESP))))))))))))))))));
/* epilogue */
_(~0U);
return buf;
@@ -134,6 +137,7 @@ enum h1_cs {
H1_CS_EMBRYONIC, /* Connection is waiting for the message headers (H1S is not NULL, not attached to a SC - Frontend connection only) */
H1_CS_UPGRADING, /* TCP>H1 upgrade in-progress (H1S is not NULL and attached to a SC - Frontend connection only) */
H1_CS_RUNNING, /* Connection fully established and the H1S is processing data (H1S is not NULL and attached to a SC) */
+ H1_CS_DRAINING, /* H1C is draining the message before destroying the H1S (H1S is not NULL but no SC attached) */
H1_CS_CLOSING, /* Send pending outgoing data and close the connection ASAP (H1S may be NULL) */
H1_CS_CLOSED, /* Connection must be closed now and H1C must be released (H1S is NULL) */
H1_CS_ENTRIES,
@@ -150,6 +154,7 @@ static inline const char *h1c_st_to_str(enum h1_cs st)
case H1_CS_EMBRYONIC: return "EMB";
case H1_CS_UPGRADING: return "UPG";
case H1_CS_RUNNING: return "RUN";
+ case H1_CS_DRAINING: return "DRN";
case H1_CS_CLOSING: return "CLI";
case H1_CS_CLOSED: return "CLD";
default: return "???";
diff --git a/include/haproxy/mux_quic-t.h b/include/haproxy/mux_quic-t.h
index abfc20a..02f8a72 100644
--- a/include/haproxy/mux_quic-t.h
+++ b/include/haproxy/mux_quic-t.h
@@ -13,6 +13,7 @@
#include <haproxy/htx-t.h>
#include <haproxy/list-t.h>
#include <haproxy/ncbuf-t.h>
+#include <haproxy/quic_fctl-t.h>
#include <haproxy/quic_frame-t.h>
#include <haproxy/quic_stream-t.h>
#include <haproxy/stconn-t.h>
@@ -30,7 +31,7 @@ enum qcs_type {
#define QC_CF_ERRL 0x00000001 /* fatal error detected locally, connection should be closed soon */
#define QC_CF_ERRL_DONE 0x00000002 /* local error properly handled, connection can be released */
-#define QC_CF_BLK_MFCTL 0x00000004 /* sending blocked due to connection flow-control */
+/* unused 0x00000004 */
#define QC_CF_CONN_FULL 0x00000008 /* no stream buffers available on connection */
#define QC_CF_APP_SHUT 0x00000010 /* Application layer shutdown done. */
#define QC_CF_ERR_CONN 0x00000020 /* fatal error reported by transport layer */
@@ -40,6 +41,7 @@ struct qcc {
uint64_t nb_sc; /* number of attached stream connectors */
uint64_t nb_hreq; /* number of in-progress http requests */
uint32_t flags; /* QC_CF_* */
+ int glitches; /* total number of glitches on this connection */
/* flow-control fields set by us enforced on our side. */
struct {
@@ -70,8 +72,7 @@ struct qcc {
} rfctl;
struct {
- uint64_t offsets; /* sum of all offsets prepared */
- uint64_t sent_offsets; /* sum of all offset sent */
+ struct quic_fctl fc; /* stream flow control applied on sending */
} tx;
uint64_t largest_bidi_r; /* largest remote bidi stream ID opened. */
@@ -83,6 +84,8 @@ struct qcc {
struct list send_retry_list; /* list of qcs eligible to send retry */
struct list send_list; /* list of qcs ready to send (STREAM, STOP_SENDING or RESET_STREAM emission) */
+ struct list fctl_list; /* list of sending qcs blocked on conn flow control */
+ struct list buf_wait_list; /* list of qcs blocked on stream desc buf */
struct wait_event wait_event; /* To be used if we're waiting for I/Os */
@@ -105,7 +108,7 @@ struct qcc {
#define QC_SF_FIN_STREAM 0x00000002 /* FIN bit must be set for last frame of the stream */
#define QC_SF_BLK_MROOM 0x00000004 /* app layer is blocked waiting for room in the qcs.tx.buf */
#define QC_SF_DETACH 0x00000008 /* sc is detached but there is remaining data to send */
-#define QC_SF_BLK_SFCTL 0x00000010 /* stream blocked due to stream flow control limit */
+/* unused 0x00000010 */
#define QC_SF_DEM_FULL 0x00000020 /* demux blocked on request channel buffer full */
#define QC_SF_READ_ABORTED 0x00000040 /* Rx closed using STOP_SENDING*/
#define QC_SF_TO_RESET 0x00000080 /* a RESET_STREAM must be sent */
@@ -155,10 +158,7 @@ struct qcs {
uint64_t msd_init; /* initial max-stream-data */
} rx;
struct {
- uint64_t offset; /* last offset of data ready to be sent */
- uint64_t sent_offset; /* last offset sent by transport layer */
- struct buffer buf; /* transmit buffer before sending via xprt */
- uint64_t msd; /* fctl bytes limit to respect on emission */
+ struct quic_fctl fc; /* stream flow control applied on sending */
} tx;
struct eb64_node by_id;
@@ -168,6 +168,8 @@ struct qcs {
struct list el; /* element of qcc.send_retry_list */
struct list el_send; /* element of qcc.send_list */
struct list el_opening; /* element of qcc.opening_list */
+ struct list el_fctl; /* element of qcc.fctl_list */
+ struct list el_buf; /* element of qcc.buf_wait_list */
struct wait_event wait_event;
struct wait_event *subs;
@@ -185,18 +187,38 @@ enum qcc_app_ops_close_side {
/* QUIC application layer operations */
struct qcc_app_ops {
+ /* Initialize <qcc> connection app context. */
int (*init)(struct qcc *qcc);
+ /* Finish connection initialization if prelude required. */
+ int (*finalize)(void *ctx);
+
+ /* Initialize <qcs> stream app context or leave it to NULL if rejected. */
int (*attach)(struct qcs *qcs, void *conn_ctx);
- ssize_t (*decode_qcs)(struct qcs *qcs, struct buffer *b, int fin);
- size_t (*snd_buf)(struct qcs *qcs, struct buffer *buf, size_t count);
+
+ /* Convert received HTTP payload to HTX. */
+ ssize_t (*rcv_buf)(struct qcs *qcs, struct buffer *b, int fin);
+
+ /* Convert HTX to HTTP payload for sending. */
+ size_t (*snd_buf)(struct qcs *qcs, struct buffer *b, size_t count);
+
+ /* Negotiate and commit fast-forward data from opposite MUX. */
size_t (*nego_ff)(struct qcs *qcs, size_t count);
size_t (*done_ff)(struct qcs *qcs);
+
+ /* Notify about <qcs> stream closure. */
int (*close)(struct qcs *qcs, enum qcc_app_ops_close_side side);
+ /* Free <qcs> stream app context. */
void (*detach)(struct qcs *qcs);
- int (*finalize)(void *ctx);
- void (*shutdown)(void *ctx); /* Close a connection. */
+
+ /* Perform graceful shutdown. */
+ void (*shutdown)(void *ctx);
+ /* Free connection app context. */
void (*release)(void *ctx);
+
+ /* Increment app counters on CONNECTION_CLOSE_APP reception. */
void (*inc_err_cnt)(void *ctx, int err_code);
+ /* Set QCC error code as suspicious activity has been detected. */
+ void (*report_susp)(void *ctx);
};
#endif /* USE_QUIC */
diff --git a/include/haproxy/mux_quic.h b/include/haproxy/mux_quic.h
index 872c5ea..1ed8ad1 100644
--- a/include/haproxy/mux_quic.h
+++ b/include/haproxy/mux_quic.h
@@ -13,19 +13,24 @@
#include <haproxy/stconn.h>
void qcc_set_error(struct qcc *qcc, int err, int app);
+int qcc_report_glitch(struct qcc *qcc, int inc);
struct qcs *qcc_init_stream_local(struct qcc *qcc, int bidi);
struct stconn *qcs_attach_sc(struct qcs *qcs, struct buffer *buf, char fin);
int qcs_is_close_local(struct qcs *qcs);
int qcs_is_close_remote(struct qcs *qcs);
-struct buffer *qcs_get_buf(struct qcs *qcs, struct buffer *bptr);
int qcs_subscribe(struct qcs *qcs, int event_type, struct wait_event *es);
void qcs_notify_recv(struct qcs *qcs);
void qcs_notify_send(struct qcs *qcs);
+int qcc_notify_buf(struct qcc *qcc);
-void qcc_emit_cc_app(struct qcc *qcc, int err, int immediate);
+struct buffer *qcc_get_stream_rxbuf(struct qcs *qcs);
+struct buffer *qcc_get_stream_txbuf(struct qcs *qcs, int *err);
+int qcc_realign_stream_txbuf(const struct qcs *qcs, struct buffer *out);
+int qcc_release_stream_txbuf(struct qcs *qcs);
+int qcc_stream_can_send(const struct qcs *qcs);
void qcc_reset_stream(struct qcs *qcs, int err);
-void qcc_send_stream(struct qcs *qcs, int urg);
+void qcc_send_stream(struct qcs *qcs, int urg, int count);
void qcc_abort_stream_read(struct qcs *qcs);
int qcc_recv(struct qcc *qcc, uint64_t id, uint64_t len, uint64_t offset,
char fin, char *data);
@@ -111,6 +116,8 @@ static inline void qcs_wait_http_req(struct qcs *qcs)
LIST_APPEND(&qcc->opening_list, &qcs->el_opening);
}
+void qcc_show_quic(struct qcc *qcc);
+
#endif /* USE_QUIC */
#endif /* _HAPROXY_MUX_QUIC_H */
diff --git a/include/haproxy/net_helper.h b/include/haproxy/net_helper.h
index f019d30..ee27ed4 100644
--- a/include/haproxy/net_helper.h
+++ b/include/haproxy/net_helper.h
@@ -91,6 +91,34 @@ static inline void write_ptr(void *p, const void *ptr)
return write_u64(p, (uintptr_t)ptr);
}
+/* Read a float in native host order */
+static inline float read_flt(const void *p)
+{
+ const union { float flt; } __attribute__((packed))*u = p;
+ return u->flt;
+}
+
+/* Write a float in native host order */
+static inline void write_flt(void *p, const float flt)
+{
+ union { float flt; } __attribute__((packed))*u = p;
+ u->flt = flt;
+}
+
+/* Read a double in native host order */
+static inline double read_dbl(const void *p)
+{
+ const union { double dbl; } __attribute__((packed))*u = p;
+ return u->dbl;
+}
+
+/* Write a double in native host order */
+static inline void write_dbl(void *p, const double dbl)
+{
+ union { double dbl; } __attribute__((packed))*u = p;
+ u->dbl = dbl;
+}
+
/* Read a possibly wrapping number of bytes <bytes> into destination <dst>. The
* first segment is composed of <s1> bytes at p1. The remaining byte(s), if any,
* are read from <p2>. <s1> may be zero and may also be larger than <bytes>. The
diff --git a/include/haproxy/openssl-compat.h b/include/haproxy/openssl-compat.h
index 5639468..d145fb4 100644
--- a/include/haproxy/openssl-compat.h
+++ b/include/haproxy/openssl-compat.h
@@ -48,6 +48,7 @@
#include <haproxy/quic_openssl_compat.h>
#endif
+
#if defined(LIBRESSL_VERSION_NUMBER)
/* LibreSSL is a fork of OpenSSL 1.0.1g but pretends to be 2.0.0, thus
* systematically breaking when some code is written for a specific version
@@ -108,6 +109,24 @@
#define HAVE_SSL_get0_verified_chain
#endif
+#if defined(SSL_OP_NO_ANTI_REPLAY)
+#define HAVE_SSL_0RTT
+#endif
+
+/* At this time, wolfssl, libressl and the openssl QUIC compatibility do not support 0-RTT */
+#if defined(HAVE_SSL_0RTT) && !defined(USE_QUIC_OPENSSL_COMPAT) && !defined(LIBRESSL_VERSION_NUMBER) && !defined(USE_OPENSSL_WOLFSSL)
+#define HAVE_SSL_0RTT_QUIC
+#endif
+
+
+#if defined(SSL_CTX_set_security_level) || HA_OPENSSL_VERSION_NUMBER >= 0x1010100fL
+#define HAVE_SSL_SET_SECURITY_LEVEL
+#endif
+
+#if !defined(HAVE_SSL_SET_SECURITY_LEVEL)
+/* define a nope function for set_security_level */
+#define SSL_CTX_set_security_level(ctx, level) ({})
+#endif
#if (HA_OPENSSL_VERSION_NUMBER >= 0x3000000fL)
#define HAVE_OSSL_PARAM
@@ -372,6 +391,10 @@ static inline unsigned long ERR_peek_error_func(const char **func)
#define EVP_CTRL_AEAD_SET_TAG EVP_CTRL_GCM_SET_TAG
#endif
+#if !defined(EVP_CTRL_AEAD_GET_TAG)
+#define EVP_CTRL_AEAD_GET_TAG EVP_CTRL_GCM_GET_TAG
+#endif
+
/* Supported hash function for TLS tickets */
#ifdef OPENSSL_NO_SHA256
#define TLS_TICKET_HASH_FUNCT EVP_sha1
@@ -483,5 +506,10 @@ static inline unsigned long ERR_peek_error_func(const char **func)
#define SSL_CTX_set1_sigalgs_list SSL_CTX_set1_sigalgs_list
#endif
+#ifndef SSL_CTX_get_tlsext_status_cb
+# define SSL_CTX_get_tlsext_status_cb(ctx, cb) \
+ *(cb) = (void (*) (void))ctx->tlsext_status_cb
+#endif
+
#endif /* USE_OPENSSL */
#endif /* _HAPROXY_OPENSSL_COMPAT_H */
diff --git a/include/haproxy/pattern-t.h b/include/haproxy/pattern-t.h
index 6c1ba24..aa3a178 100644
--- a/include/haproxy/pattern-t.h
+++ b/include/haproxy/pattern-t.h
@@ -92,9 +92,11 @@ enum {
PAT_MATCH_NUM
};
-#define PAT_REF_MAP 0x1 /* Set if the reference is used by at least one map. */
-#define PAT_REF_ACL 0x2 /* Set if the reference is used by at least one acl. */
-#define PAT_REF_SMP 0x4 /* Flag used if the reference contains a sample. */
+#define PAT_REF_MAP 0x01 /* Set if the reference is used by at least one map. */
+#define PAT_REF_ACL 0x02 /* Set if the reference is used by at least one acl. */
+#define PAT_REF_SMP 0x04 /* Flag used if the reference contains a sample. */
+#define PAT_REF_FILE 0x08 /* Set if the reference was loaded from a file */
+#define PAT_REF_ID 0x10 /* Set if the reference is only an ID (not loaded from a file) */
/* This struct contain a list of reference strings for dunamically
* updatable patterns.
diff --git a/include/haproxy/peers-t.h b/include/haproxy/peers-t.h
index 124fac3..19619d0 100644
--- a/include/haproxy/peers-t.h
+++ b/include/haproxy/peers-t.h
@@ -34,6 +34,102 @@
#include <haproxy/stick_table-t.h>
#include <haproxy/thread-t.h>
+/* peer state with respects of its applet, as seen from outside */
+enum peer_app_state {
+ PEER_APP_ST_STOPPED = 0, /* The peer has no applet */
+ PEER_APP_ST_STARTING, /* The peer has an applet with a validated connection but sync task must ack it first */
+ PEER_APP_ST_RUNNING, /* The starting state was processed by the sync task and the peer can process messages */
+ PEER_APP_ST_STOPPING, /* The peer applet was released but the sync task must ack it before switching the peer in STOPPED state */
+};
+
+/* peer learn state */
+enum peer_learn_state {
+ PEER_LR_ST_NOTASSIGNED = 0,/* The peer is not assigned for a leason */
+ PEER_LR_ST_ASSIGNED, /* The peer is assigned for a leason */
+ PEER_LR_ST_PROCESSING, /* The peer has started the leason and it is not finished */
+ PEER_LR_ST_FINISHED, /* The peer has finished the leason, this state must be ack by the sync task */
+};
+
+/******************************/
+/* peers section resync flags */
+/******************************/
+#define PEERS_F_RESYNC_LOCAL_FINISHED 0x00000001 /* Learn from local peer finished or no more needed */
+#define PEERS_F_RESYNC_REMOTE_FINISHED 0x00000002 /* Learn from remote peer finished or no more needed */
+#define PEERS_F_RESYNC_ASSIGN 0x00000004 /* A peer was assigned to learn our lesson */
+/* unused 0x00000008..0x00080000 */
+#define PEERS_F_DBG_RESYNC_LOCALTIMEOUT 0x00100000 /* Timeout waiting for a full resync from a local node was experienced at lest once (for debugging purpose) */
+#define PEERS_F_DBG_RESYNC_REMOTETIMEOUT 0x00200000 /* Timeout waiting for a full resync from a remote node was experienced at lest once (for debugging purpose) */
+#define PEERS_F_DBG_RESYNC_LOCALABORT 0x00400000 /* Session aborted learning from a local node was experienced at lest once (for debugging purpose) */
+#define PEERS_F_DBG_RESYNC_REMOTEABORT 0x00800000 /* Session aborted learning from a remote node was experienced at lest once (for debugging purpose) */
+#define PEERS_F_DBG_RESYNC_LOCALFINISHED 0x01000000 /* A fully up to date local node teach us at lest once (for debugging purpose) */
+#define PEERS_F_DBG_RESYNC_REMOTEFINISHED 0x02000000 /* A fully up to remote node teach us at lest once (for debugging purpose) */
+#define PEERS_F_DBG_RESYNC_LOCALPARTIAL 0x04000000 /* A partially up to date local node teach us at lest once (for debugging purpose) */
+#define PEERS_F_DBG_RESYNC_REMOTEPARTIAL 0x08000000 /* A partially up to date remote node teach us at lest once (for debugging purpose) */
+#define PEERS_F_DBG_RESYNC_LOCALASSIGN 0x10000000 /* A local node was assigned for a full resync at lest once (for debugging purpose) */
+#define PEERS_F_DBG_RESYNC_REMOTEASSIGN 0x20000000 /* A remote node was assigned for a full resync at lest once (for debugging purpose) */
+
+#define PEERS_RESYNC_FROMLOCAL 0x00000000 /* No resync finished, must be performed from local first */
+#define PEERS_RESYNC_FROMREMOTE PEERS_F_RESYNC_LOCAL_FINISHED /* Resync from local peer finished, must be performed from remote peer now */
+#define PEERS_RESYNC_STATEMASK (PEERS_F_RESYNC_LOCAL_FINISHED|PEERS_F_RESYNC_REMOTE_FINISHED)
+#define PEERS_RESYNC_FINISHED (PEERS_F_RESYNC_LOCAL_FINISHED|PEERS_F_RESYNC_REMOTE_FINISHED)
+
+/* This function is used to report flags in debugging tools. Please reflect
+ * below any single-bit flag addition above in the same order via the
+ * __APPEND_FLAG macro. The new end of the buffer is returned.
+ */
+static forceinline char *peers_show_flags(char *buf, size_t len, const char *delim, uint flg)
+{
+#define _(f, ...) __APPEND_FLAG(buf, len, delim, flg, f, #f, __VA_ARGS__)
+ /* prologue */
+ _(0);
+ /* flags */
+ _(PEERS_F_RESYNC_LOCAL_FINISHED, _(PEERS_F_RESYNC_REMOTE_FINISHED, _(PEERS_F_RESYNC_ASSIGN,
+ _(PEERS_F_DBG_RESYNC_LOCALTIMEOUT, _(PEERS_F_DBG_RESYNC_REMOTETIMEOUT,
+ _(PEERS_F_DBG_RESYNC_LOCALABORT, _(PEERS_F_DBG_RESYNC_REMOTEABORT,
+ _(PEERS_F_DBG_RESYNC_LOCALFINISHED, _(PEERS_F_DBG_RESYNC_REMOTEFINISHED,
+ _(PEERS_F_DBG_RESYNC_LOCALPARTIAL, _(PEERS_F_DBG_RESYNC_REMOTEPARTIAL,
+ _(PEERS_F_DBG_RESYNC_LOCALASSIGN, _(PEERS_F_DBG_RESYNC_REMOTEABORT)))))))))))));
+ /* epilogue */
+ _(~0U);
+ return buf;
+#undef _
+}
+
+/******************************/
+/* Peer flags */
+/******************************/
+#define PEER_F_TEACH_PROCESS 0x00000001 /* Teach a lesson to current peer */
+#define PEER_F_TEACH_FINISHED 0x00000002 /* Teach conclude, (wait for confirm) */
+#define PEER_F_LOCAL_TEACH_COMPLETE 0x00000004 /* The old local peer taught all that it known to new one */
+#define PEER_F_LEARN_NOTUP2DATE 0x00000008 /* Learn from peer finished but peer is not up to date */
+#define PEER_F_WAIT_SYNCTASK_ACK 0x00000010 /* Stop all processing waiting for the sync task acknowledgement when the applet state changes */
+#define PEER_F_ALIVE 0x00000020 /* Used to flag a peer a alive. */
+#define PEER_F_HEARTBEAT 0x00000040 /* Heartbeat message to send. */
+#define PEER_F_DWNGRD 0x00000080 /* When this flag is enabled, we must downgrade the supported version announced during peer sessions. */
+/* unused 0x00000100..0x00080000 */
+#define PEER_F_DBG_RESYNC_REQUESTED 0x00100000 /* A resnyc was explicitly requested at least once (for debugging purpose) */
+
+#define PEER_TEACH_FLAGS (PEER_F_TEACH_PROCESS|PEER_F_TEACH_FINISHED)
+
+/* This function is used to report flags in debugging tools. Please reflect
+ * below any single-bit flag addition above in the same order via the
+ * __APPEND_FLAG macro. The new end of the buffer is returned.
+ */
+static forceinline char *peer_show_flags(char *buf, size_t len, const char *delim, uint flg)
+{
+#define _(f, ...) __APPEND_FLAG(buf, len, delim, flg, f, #f, __VA_ARGS__)
+ /* prologue */
+ _(0);
+ /* flags */
+ _(PEER_F_TEACH_PROCESS, _(PEER_F_TEACH_FINISHED, _(PEER_F_LOCAL_TEACH_COMPLETE,
+ _(PEER_F_LEARN_NOTUP2DATE, _(PEER_F_WAIT_SYNCTASK_ACK,
+ _(PEER_F_ALIVE, _(PEER_F_HEARTBEAT, _(PEER_F_DWNGRD,
+ _(PEER_F_DBG_RESYNC_REQUESTED)))))))));
+ /* epilogue */
+ _(~0U);
+ return buf;
+#undef _
+}
struct shared_table {
struct stktable *table; /* stick table to sync */
@@ -52,6 +148,8 @@ struct shared_table {
struct peer {
int local; /* proxy state */
+ enum peer_app_state appstate; /* peer app state */
+ enum peer_learn_state learnstate; /* peer learn state */
__decl_thread(HA_SPINLOCK_T lock); /* lock used to handle this peer section */
char *id;
struct {
@@ -59,10 +157,6 @@ struct peer {
int line; /* line where the section appears */
} conf; /* config information */
time_t last_change;
- struct sockaddr_storage addr; /* peer address */
- struct protocol *proto; /* peer address protocol */
- struct xprt_ops *xprt; /* peer socket operations at transport layer */
- void *sock_init_arg; /* socket operations's opaque init argument if needed */
unsigned int flags; /* peer session flags */
unsigned int statuscode; /* current/last session status code */
unsigned int reconnect; /* next connect timer */
diff --git a/include/haproxy/peers.h b/include/haproxy/peers.h
index e3c5fd3..d100c0c 100644
--- a/include/haproxy/peers.h
+++ b/include/haproxy/peers.h
@@ -40,30 +40,10 @@ int peers_register_table(struct peers *, struct stktable *table);
void peers_setup_frontend(struct proxy *fe);
void peers_register_keywords(struct peers_kw_list *pkwl);
-#if defined(USE_OPENSSL)
static inline enum obj_type *peer_session_target(struct peer *p, struct stream *s)
{
- if (p->srv->use_ssl)
- return &p->srv->obj_type;
- else
- return &s->be->obj_type;
+ return &p->srv->obj_type;
}
-static inline struct xprt_ops *peer_xprt(struct peer *p)
-{
- return p->srv->use_ssl ? xprt_get(XPRT_SSL) : xprt_get(XPRT_RAW);
-}
-#else
-static inline enum obj_type *peer_session_target(struct peer *p, struct stream *s)
-{
- return &s->be->obj_type;
-}
-
-static inline struct xprt_ops *peer_xprt(struct peer *p)
-{
- return xprt_get(XPRT_RAW);
-}
-#endif
-
#endif /* _HAPROXY_PEERS_H */
diff --git a/include/haproxy/pool.h b/include/haproxy/pool.h
index bf7cb8d..66ad292 100644
--- a/include/haproxy/pool.h
+++ b/include/haproxy/pool.h
@@ -77,7 +77,7 @@
if (likely(!(pool_debugging & POOL_DBG_TAG))) \
break; \
if (*(typeof(pool)*)(((char *)__i) + __p->size) != __p) { \
- pool_inspect_item("tag mismatch on free()", pool, item, caller); \
+ pool_inspect_item("tag mismatch on free()", __p, __i, caller, -1); \
ABORT_NOW(); \
} \
} while (0)
@@ -126,7 +126,7 @@ void *pool_destroy(struct pool_head *pool);
void pool_destroy_all(void);
void *__pool_alloc(struct pool_head *pool, unsigned int flags);
void __pool_free(struct pool_head *pool, void *ptr);
-void pool_inspect_item(const char *msg, struct pool_head *pool, const void *item, const void *caller);
+void pool_inspect_item(const char *msg, struct pool_head *pool, const void *item, const void *caller, ssize_t ofs);
/****************** Thread-local cache management ******************/
diff --git a/include/haproxy/proto_quic.h b/include/haproxy/proto_quic.h
index a0e2b98..b420f35 100644
--- a/include/haproxy/proto_quic.h
+++ b/include/haproxy/proto_quic.h
@@ -21,6 +21,10 @@
#ifndef _HAPROXY_PROTO_QUIC_H
#define _HAPROXY_PROTO_QUIC_H
+#include <import/ebtree.h>
+#include <haproxy/api-t.h>
+#include <haproxy/thread-t.h>
+
extern struct protocol proto_quic4;
extern struct protocol proto_quic6;
diff --git a/include/haproxy/proto_rhttp.h b/include/haproxy/proto_rhttp.h
index 421680f..6676e04 100644
--- a/include/haproxy/proto_rhttp.h
+++ b/include/haproxy/proto_rhttp.h
@@ -10,6 +10,7 @@ int rhttp_bind_receiver(struct receiver *rx, char **errmsg);
int rhttp_bind_listener(struct listener *listener, char *errmsg, int errlen);
void rhttp_enable_listener(struct listener *l);
void rhttp_disable_listener(struct listener *l);
+int rhttp_suspend_listener(struct listener *l);
struct connection *rhttp_accept_conn(struct listener *l, int *status);
void rhttp_unbind_receiver(struct listener *l);
int rhttp_set_affinity(struct connection *conn, int new_tid);
diff --git a/include/haproxy/proto_sockpair.h b/include/haproxy/proto_sockpair.h
index bb0256e..e8cb2ac 100644
--- a/include/haproxy/proto_sockpair.h
+++ b/include/haproxy/proto_sockpair.h
@@ -21,6 +21,8 @@
#ifndef _HAPROXY_PROTO_SOCKPAIR_H
#define _HAPROXY_PROTO_SOCKPAIR_H
+#include <haproxy/receiver-t.h>
+
extern struct proto_fam proto_fam_sockpair;
extern struct protocol proto_sockpair;
diff --git a/include/haproxy/proto_udp.h b/include/haproxy/proto_udp.h
index 1c4da77..6475bf9 100644
--- a/include/haproxy/proto_udp.h
+++ b/include/haproxy/proto_udp.h
@@ -24,6 +24,8 @@
#ifndef _PROTO_PROTO_UDP_H
#define _PROTO_PROTO_UDP_H
+#include <haproxy/receiver-t.h>
+
extern struct protocol proto_udp4;
extern struct protocol proto_udp6;
diff --git a/include/haproxy/protobuf.h b/include/haproxy/protobuf.h
index 009bd13..512288b 100644
--- a/include/haproxy/protobuf.h
+++ b/include/haproxy/protobuf.h
@@ -365,13 +365,13 @@ int protobuf_smp_store_64bit(struct sample *smp, int type,
case PBUF_T_64BIT_FIXED64:
case PBUF_T_64BIT_SFIXED64:
smp->data.type = SMP_T_SINT;
- smp->data.u.sint = pbuf_le64toh(*(uint64_t *)pos);
+ smp->data.u.sint = pbuf_le64toh(read_u64(pos));
smp->flags = SMP_F_VOL_TEST;
break;
case PBUF_T_64BIT_DOUBLE:
smp->data.type = SMP_T_SINT;
- smp->data.u.sint = pbuf_le64toh(*(double *)pos);
+ smp->data.u.sint = pbuf_le64toh(read_dbl(pos));
smp->flags = SMP_F_VOL_TEST;
break;
@@ -455,19 +455,19 @@ int protobuf_smp_store_32bit(struct sample *smp, int type,
case PBUF_T_32BIT_FIXED32:
smp->data.type = SMP_T_SINT;
- smp->data.u.sint = pbuf_le32toh(*(uint32_t *)pos);
+ smp->data.u.sint = pbuf_le32toh(read_u32(pos));
smp->flags = SMP_F_VOL_TEST;
break;
case PBUF_T_32BIT_SFIXED32:
smp->data.type = SMP_T_SINT;
- smp->data.u.sint = (int32_t)pbuf_le32toh(*(uint32_t *)pos);
+ smp->data.u.sint = (int32_t)pbuf_le32toh(read_u32(pos));
smp->flags = SMP_F_VOL_TEST;
break;
case PBUF_T_32BIT_FLOAT:
smp->data.type = SMP_T_SINT;
- smp->data.u.sint = pbuf_le32toh(*(float *)pos);
+ smp->data.u.sint = pbuf_le32toh(read_flt(pos));
smp->flags = SMP_F_VOL_TEST;
break;
diff --git a/include/haproxy/protocol-t.h b/include/haproxy/protocol-t.h
index b85f29c..0c5bd9e 100644
--- a/include/haproxy/protocol-t.h
+++ b/include/haproxy/protocol-t.h
@@ -138,6 +138,17 @@ struct protocol {
struct list list; /* list of registered protocols (under proto_lock) */
};
+/* Transport protocol identifiers which can be used as masked values. */
+enum ha_proto {
+ HA_PROTO_NONE = 0x00,
+
+ HA_PROTO_TCP = 0x01,
+ HA_PROTO_UDP = 0x02,
+ HA_PROTO_QUIC = 0x04,
+
+ HA_PROTO_ANY = 0xff,
+};
+
#endif /* _HAPROXY_PROTOCOL_T_H */
/*
diff --git a/include/haproxy/proxy-t.h b/include/haproxy/proxy-t.h
index 2f7bf7b..f6ed211 100644
--- a/include/haproxy/proxy-t.h
+++ b/include/haproxy/proxy-t.h
@@ -34,7 +34,7 @@
#include <haproxy/backend-t.h>
#include <haproxy/compression-t.h>
#include <haproxy/counters-t.h>
-#include <haproxy/freq_ctr-t.h>
+#include <haproxy/guid-t.h>
#include <haproxy/obj_type-t.h>
#include <haproxy/queue-t.h>
#include <haproxy/server-t.h>
@@ -92,11 +92,11 @@ enum PR_SRV_STATE_FILE {
#define PR_O_IGNORE_PRB 0x00000200 /* ignore empty requests (aborts and timeouts) */
#define PR_O_NULLNOLOG 0x00000400 /* a connect without request will not be logged */
#define PR_O_WREQ_BODY 0x00000800 /* always wait for the HTTP request body */
-#define PR_O_HTTP_UPG 0x00001000 /* Contain a "switch-mode http" tcp-request rule */
+#define PR_O_HTTP_UPG 0x00001000 /* implicit (default/use backend) or explicit (switch-mode) http upgrade */
/* unused: 0x00002000 */
#define PR_O_PERSIST 0x00004000 /* server persistence stays effective even when server is down */
#define PR_O_LOGASAP 0x00008000 /* log as soon as possible, without waiting for the stream to complete */
-#define PR_O_ERR_LOGFMT 0x00010000 /* use log-format for connection error message */
+/* unused: 0x00010000 */
#define PR_O_CHK_CACHE 0x00020000 /* require examination of cacheability of the 'set-cookie' field */
#define PR_O_TCP_CLI_KA 0x00040000 /* enable TCP keep-alive on client-side streams */
#define PR_O_TCP_SRV_KA 0x00080000 /* enable TCP keep-alive on server-side streams */
@@ -214,6 +214,7 @@ enum PR_SRV_STATE_FILE {
#define PR_FL_EXPLICIT_REF 0x08 /* The default proxy is explicitly referenced by another proxy */
#define PR_FL_IMPLICIT_REF 0x10 /* The default proxy is implicitly referenced by another proxy */
#define PR_FL_PAUSED 0x20 /* The proxy was paused at run time (reversible) */
+#define PR_FL_CHECKED 0x40 /* The proxy configuration was fully checked (including postparsing checks) */
struct stream;
@@ -352,10 +353,6 @@ struct proxy {
struct queue queue; /* queued requests (pendconns) */
int totpend; /* total number of pending connections on this instance (for stats) */
unsigned int feconn, beconn; /* # of active frontend and backends streams */
- struct freq_ctr fe_req_per_sec; /* HTTP requests per second on the frontend */
- struct freq_ctr fe_conn_per_sec; /* received connections per second on the frontend */
- struct freq_ctr fe_sess_per_sec; /* accepted sessions per second on the frontend (after tcp rules) */
- struct freq_ctr be_sess_per_sec; /* sessions per second on the backend */
unsigned int fe_sps_lim; /* limit on new sessions per second on the frontend */
unsigned int fullconn; /* #conns on backend above which servers are used at full load */
unsigned int tot_fe_maxconn; /* #maxconn of frontends linked to that backend, it is used to compute fullconn */
@@ -363,9 +360,7 @@ struct proxy {
int conn_retries; /* maximum number of connect retries */
unsigned int retry_type; /* Type of retry allowed */
int redispatch_after; /* number of retries before redispatch */
- unsigned down_trans; /* up-down transitions */
unsigned down_time; /* total time the proxy was down */
- time_t last_change; /* last time, when the state was changed */
int (*accept)(struct stream *s); /* application layer's accept() */
struct conn_src conn_src; /* connection source settings */
enum obj_type *default_target; /* default target to use for accepted streams or NULL */
@@ -373,12 +368,12 @@ struct proxy {
struct proxy *next_stkt_ref; /* Link to the list of proxies which refer to the same stick-table. */
struct list loggers; /* one per 'log' directive */
- struct list logformat; /* log_format linked list */
- struct list logformat_sd; /* log_format linked list for the RFC5424 structured-data part */
- struct list logformat_error; /* log_format linked list used in case of connection error on the frontend */
+ struct lf_expr logformat; /* log_format linked list */
+ struct lf_expr logformat_sd; /* log_format linked list for the RFC5424 structured-data part */
+ struct lf_expr logformat_error; /* log_format linked list used in case of connection error on the frontend */
struct buffer log_tag; /* override default syslog tag */
struct ist header_unique_id; /* unique-id header */
- struct list format_unique_id; /* unique-id format */
+ struct lf_expr format_unique_id; /* unique-id format */
int to_log; /* things to be logged (LW_*) */
int nb_req_cap, nb_rsp_cap; /* # of headers to be captured */
struct cap_hdr *req_cap; /* chained list of request headers to be captured */
@@ -426,18 +421,7 @@ struct proxy {
struct arg_list args; /* sample arg list that need to be resolved */
unsigned int refcount; /* refcount on this proxy (only used for default proxy for now) */
struct ebpt_node by_name; /* proxies are stored sorted by name here */
- char *logformat_string; /* log format string */
- char *lfs_file; /* file name where the logformat string appears (strdup) */
- int lfs_line; /* file name where the logformat string appears */
- int uif_line; /* file name where the unique-id-format string appears */
- char *uif_file; /* file name where the unique-id-format string appears (strdup) */
- char *uniqueid_format_string; /* unique-id format string */
- char *logformat_sd_string; /* log format string for the RFC5424 structured-data part */
- char *lfsd_file; /* file name where the structured-data logformat string for RFC5424 appears (strdup) */
- int lfsd_line; /* file name where the structured-data logformat string for RFC5424 appears */
- char *error_logformat_string;
- char *elfs_file;
- int elfs_line;
+ struct list lf_checks; /* list of logformats found in the proxy section that needs to be checked during postparse */
} conf; /* config information */
struct http_ext *http_ext; /* http ext options */
struct eb_root used_server_addr; /* list of server addresses in use */
@@ -467,6 +451,8 @@ struct proxy {
*/
struct list filter_configs; /* list of the filters that are declared on this proxy */
+ struct guid_node guid; /* GUID global tree node */
+
EXTRA_COUNTERS(extra_counters_fe);
EXTRA_COUNTERS(extra_counters_be);
};
@@ -478,7 +464,7 @@ struct switching_rule {
union {
struct proxy *backend; /* target backend */
char *name; /* target backend name during config parsing */
- struct list expr; /* logformat expression to use for dynamic rules */
+ struct lf_expr expr; /* logformat expression to use for dynamic rules */
} be;
char *file;
int line;
@@ -492,7 +478,7 @@ struct server_rule {
struct server *ptr; /* target server */
char *name; /* target server name during config parsing */
} srv;
- struct list expr; /* logformat expression to use for dynamic rules */
+ struct lf_expr expr; /* logformat expression to use for dynamic rules */
char *file;
int line;
};
@@ -521,7 +507,7 @@ struct redirect_rule {
int type;
int rdr_len;
char *rdr_str;
- struct list rdr_fmt;
+ struct lf_expr rdr_fmt;
int code;
unsigned int flags;
int cookie_len;
diff --git a/include/haproxy/proxy.h b/include/haproxy/proxy.h
index efdfa21..974c78a 100644
--- a/include/haproxy/proxy.h
+++ b/include/haproxy/proxy.h
@@ -59,8 +59,6 @@ struct proxy *proxy_find_by_id(int id, int cap, int table);
struct proxy *proxy_find_by_name(const char *name, int cap, int table);
struct proxy *proxy_find_best_match(int cap, const char *name, int id, int *diff);
struct server *findserver(const struct proxy *px, const char *name);
-struct server *findserver_unique_id(const struct proxy *px, int puid, uint32_t rid);
-struct server *findserver_unique_name(const struct proxy *px, const char *name, uint32_t rid);
int proxy_cfg_ensure_no_http(struct proxy *curproxy);
int proxy_cfg_ensure_no_log(struct proxy *curproxy);
void init_new_proxy(struct proxy *p);
@@ -136,7 +134,7 @@ static inline void proxy_inc_fe_conn_ctr(struct listener *l, struct proxy *fe)
if (l && l->counters)
_HA_ATOMIC_INC(&l->counters->cum_conn);
HA_ATOMIC_UPDATE_MAX(&fe->fe_counters.cps_max,
- update_freq_ctr(&fe->fe_conn_per_sec, 1));
+ update_freq_ctr(&fe->fe_counters.conn_per_sec, 1));
}
/* increase the number of cumulated connections accepted by the designated frontend */
@@ -147,7 +145,7 @@ static inline void proxy_inc_fe_sess_ctr(struct listener *l, struct proxy *fe)
if (l && l->counters)
_HA_ATOMIC_INC(&l->counters->cum_sess);
HA_ATOMIC_UPDATE_MAX(&fe->fe_counters.sps_max,
- update_freq_ctr(&fe->fe_sess_per_sec, 1));
+ update_freq_ctr(&fe->fe_counters.sess_per_sec, 1));
}
/* increase the number of cumulated HTTP sessions on the designated frontend.
@@ -165,12 +163,12 @@ static inline void proxy_inc_fe_cum_sess_ver_ctr(struct listener *l, struct prox
_HA_ATOMIC_INC(&l->counters->cum_sess_ver[http_ver - 1]);
}
-/* increase the number of cumulated connections on the designated backend */
+/* increase the number of cumulated streams on the designated backend */
static inline void proxy_inc_be_ctr(struct proxy *be)
{
- _HA_ATOMIC_INC(&be->be_counters.cum_conn);
+ _HA_ATOMIC_INC(&be->be_counters.cum_sess);
HA_ATOMIC_UPDATE_MAX(&be->be_counters.sps_max,
- update_freq_ctr(&be->be_sess_per_sec, 1));
+ update_freq_ctr(&be->be_counters.sess_per_sec, 1));
}
/* increase the number of cumulated requests on the designated frontend.
@@ -187,7 +185,7 @@ static inline void proxy_inc_fe_req_ctr(struct listener *l, struct proxy *fe,
if (l && l->counters)
_HA_ATOMIC_INC(&l->counters->p.http.cum_req[http_ver]);
HA_ATOMIC_UPDATE_MAX(&fe->fe_counters.p.http.rps_max,
- update_freq_ctr(&fe->fe_req_per_sec, 1));
+ update_freq_ctr(&fe->fe_counters.req_per_sec, 1));
}
/* Returns non-zero if the proxy is configured to retry a request if we got that status, 0 otherwise */
diff --git a/include/haproxy/qmux_http.h b/include/haproxy/qmux_http.h
index a7dbe7c..4a77114 100644
--- a/include/haproxy/qmux_http.h
+++ b/include/haproxy/qmux_http.h
@@ -10,7 +10,6 @@ size_t qcs_http_rcv_buf(struct qcs *qcs, struct buffer *buf, size_t count,
char *fin);
size_t qcs_http_snd_buf(struct qcs *qcs, struct buffer *buf, size_t count,
char *fin);
-size_t qcs_http_reset_buf(struct qcs *qcs, struct buffer *buf, size_t count);
#endif /* USE_QUIC */
diff --git a/include/haproxy/qpack-dec.h b/include/haproxy/qpack-dec.h
index 993f450..f0f531f 100644
--- a/include/haproxy/qpack-dec.h
+++ b/include/haproxy/qpack-dec.h
@@ -21,6 +21,8 @@
#ifndef _HAPROXY_QPACK_DEC_H
#define _HAPROXY_QPACK_DEC_H
+#include <inttypes.h>
+
struct buffer;
struct http_hdr;
@@ -28,12 +30,13 @@ struct http_hdr;
*Nothing to see with the RFC.
*/
enum {
- QPACK_ERR_NONE = 0, /* no error */
- QPACK_ERR_RIC, /* cannot decode Required Insert Count prefix field */
- QPACK_ERR_DB, /* cannot decode Delta Base prefix field */
- QPACK_ERR_TRUNCATED, /* truncated stream */
- QPACK_ERR_HUFFMAN, /* huffman decoding error */
- QPACK_ERR_TOO_LARGE, /* decoded request/response is too large */
+ QPACK_RET_NONE = 0, /* no error */
+ QPACK_RET_DECOMP, /* corresponds to RFC 9204 decompression error */
+ QPACK_RET_RIC, /* cannot decode Required Insert Count prefix field */
+ QPACK_RET_DB, /* cannot decode Delta Base prefix field */
+ QPACK_RET_TRUNCATED, /* truncated stream */
+ QPACK_RET_HUFFMAN, /* huffman decoding error */
+ QPACK_RET_TOO_LARGE, /* decoded request/response is too large */
};
struct qpack_dec {
@@ -48,4 +51,6 @@ int qpack_decode_fs(const unsigned char *buf, uint64_t len, struct buffer *tmp,
int qpack_decode_enc(struct buffer *buf, int fin, void *ctx);
int qpack_decode_dec(struct buffer *buf, int fin, void *ctx);
+int qpack_err_decode(const int value);
+
#endif /* _HAPROXY_QPACK_DEC_H */
diff --git a/include/haproxy/qpack-t.h b/include/haproxy/qpack-t.h
index 0e1736a..1cc6dab 100644
--- a/include/haproxy/qpack-t.h
+++ b/include/haproxy/qpack-t.h
@@ -43,5 +43,12 @@
#define QPACK_DEC_INST_SCCL 0x40 // Stream Cancellation
#define QPACK_DEC_INST_SACK 0x80 // Section Acknowledgment
+/* RFC 9204 6. Error Handling */
+enum qpack_err {
+ QPACK_ERR_DECOMPRESSION_FAILED = 0x200,
+ QPACK_ERR_ENCODER_STREAM_ERROR = 0x201,
+ QPACK_ERR_DECODER_STREAM_ERROR = 0x202,
+};
+
#endif /* USE_QUIC */
#endif /* _HAPROXY_QPACK_T_H */
diff --git a/include/haproxy/qpack-tbl-t.h b/include/haproxy/qpack-tbl-t.h
index c27c623..7b3e2f9 100644
--- a/include/haproxy/qpack-tbl-t.h
+++ b/include/haproxy/qpack-tbl-t.h
@@ -26,6 +26,8 @@
#ifndef _HAPROXY_QPACK_TBL_T_H
#define _HAPROXY_QPACK_TBL_T_H
+#include <inttypes.h>
+
/*
* Gcc before 3.0 needs [0] to declare a variable-size array
*/
diff --git a/include/haproxy/queue-t.h b/include/haproxy/queue-t.h
index 8f6a1ec..7d9c31c 100644
--- a/include/haproxy/queue-t.h
+++ b/include/haproxy/queue-t.h
@@ -24,6 +24,7 @@
#include <import/ebtree-t.h>
#include <haproxy/api-t.h>
+#include <haproxy/thread-t.h>
struct proxy;
struct server;
diff --git a/include/haproxy/quic_ack-t.h b/include/haproxy/quic_ack-t.h
index 95b77f1..64182e6 100644
--- a/include/haproxy/quic_ack-t.h
+++ b/include/haproxy/quic_ack-t.h
@@ -13,6 +13,10 @@
#ifndef _HAPROXY_QUIC_ACK_T_H
#define _HAPROXY_QUIC_ACK_T_H
+#include <inttypes.h>
+#include <stddef.h>
+#include <import/eb64tree.h>
+
/* The maximum number of ack ranges to be built in ACK frames */
#define QUIC_MAX_ACK_RANGES 32
diff --git a/include/haproxy/quic_ack.h b/include/haproxy/quic_ack.h
index 540e2c0..baa27ac 100644
--- a/include/haproxy/quic_ack.h
+++ b/include/haproxy/quic_ack.h
@@ -13,6 +13,12 @@
#ifndef _HAPROXY_QUIC_ACK_H
#define _HAPROXY_QUIC_ACK_H
+#include <inttypes.h>
+
+struct quic_conn;
+struct quic_arng;
+struct quic_arngs;
+
void quic_free_arngs(struct quic_conn *qc, struct quic_arngs *arngs);
int quic_update_ack_ranges_list(struct quic_conn *qc,
struct quic_arngs *arngs,
diff --git a/include/haproxy/quic_cc-t.h b/include/haproxy/quic_cc-t.h
index 888efca..e678172 100644
--- a/include/haproxy/quic_cc-t.h
+++ b/include/haproxy/quic_cc-t.h
@@ -46,6 +46,8 @@ extern unsigned long long last_ts;
enum quic_cc_algo_state_type {
/* Slow start. */
QUIC_CC_ST_SS,
+ /* Conservative slow start (HyStart++ only) */
+ QUIC_CC_ST_CS,
/* Congestion avoidance. */
QUIC_CC_ST_CA,
/* Recovery period. */
@@ -66,6 +68,7 @@ struct quic_cc_event {
union {
struct ack {
uint64_t acked;
+ uint64_t pn;
unsigned int time_sent;
} ack;
struct loss {
@@ -84,7 +87,7 @@ struct quic_cc {
/* <conn> is there only for debugging purpose. */
struct quic_conn *qc;
struct quic_cc_algo *algo;
- uint32_t priv[16];
+ uint32_t priv[18];
};
struct quic_cc_path {
@@ -117,6 +120,7 @@ struct quic_cc_algo {
void (*event)(struct quic_cc *cc, struct quic_cc_event *ev);
void (*slow_start)(struct quic_cc *cc);
void (*state_trace)(struct buffer *buf, const struct quic_cc *cc);
+ void (*hystart_start_round)(struct quic_cc *cc, uint64_t pn);
};
#endif /* USE_QUIC */
diff --git a/include/haproxy/quic_cc_hystart.h b/include/haproxy/quic_cc_hystart.h
new file mode 100644
index 0000000..4ed122c
--- /dev/null
+++ b/include/haproxy/quic_cc_hystart.h
@@ -0,0 +1,129 @@
+/* RFC 9406: HyStart++: Modified Slow Start for TCP. */
+
+/* HyStart++ constants */
+#define HYSTART_MIN_RTT_THRESH 4U /* ms */
+#define HYSTART_MAX_RTT_THRESH 16U /* ms */
+#define HYSTART_MIN_RTT_DIVISOR 8
+#define HYSTART_N_RTT_SAMPLE 8
+#define HYSTART_CSS_GROWTH_DIVISOR 4
+#define HYSTART_CSS_ROUNDS 5
+#define HYSTART_LIMIT 8 /* Must be infinite if paced */
+
+#define QUIC_CLAMP(a, b, c) ({ \
+ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ typeof(c) _c = (c); \
+ (void) (&_a == &_b); \
+ (void) (&_b == &_c); \
+ _b < _a ? _a : _b > _c ? _c : _b; })
+
+struct quic_hystart {
+ /* Current round minimum RTT. */
+ uint32_t curr_rnd_min_rtt;
+ /* Last round minimum RTT. */
+ uint32_t last_rnd_min_rtt;
+ /* Conservative Slow State baseline minimum RTT */
+ uint32_t css_baseline_min_rtt;
+ uint32_t rtt_sample_count;
+ uint32_t css_rnd_count;
+ uint64_t wnd_end;
+};
+
+/* Reset <h> Hystart++ algorithm state.
+ * Never fail.
+ */
+static inline void quic_cc_hystart_reset(struct quic_hystart *h)
+{
+ h->curr_rnd_min_rtt = UINT32_MAX;
+ h->last_rnd_min_rtt = UINT32_MAX;
+ h->css_baseline_min_rtt = UINT32_MAX;
+ h->rtt_sample_count = 0;
+ h->css_rnd_count = 0;
+ h->wnd_end = UINT64_MAX;
+}
+
+/* Track the minimum RTT. */
+static inline void quic_cc_hystart_track_min_rtt(struct quic_cc *cc,
+ struct quic_hystart *h,
+ unsigned int latest_rtt)
+{
+ if (h->wnd_end == UINT64_MAX)
+ return;
+
+ h->curr_rnd_min_rtt = QUIC_MIN(h->curr_rnd_min_rtt, latest_rtt);
+ h->rtt_sample_count++;
+}
+
+/* RFC 9406 4.2. Algorithm Details
+ * At the start of each round during standard slow start [RFC5681] and CSS,
+ * initialize the variables used to compute the last round's and current round's
+ * minimum RTT.
+ *
+ * Never fail.
+ */
+static inline void quic_cc_hystart_start_round(struct quic_hystart *h, uint64_t pn)
+{
+ if (h->wnd_end != UINT64_MAX) {
+ /* Round already started */
+ return;
+ }
+
+ h->wnd_end = pn;
+ h->last_rnd_min_rtt = h->curr_rnd_min_rtt;
+ h->rtt_sample_count = 0;
+}
+
+/* RFC 9406 4.2. Algorithm Details
+ * For rounds where at least N_RTT_SAMPLE RTT samples have been obtained and
+ * currentRoundMinRTT and lastRoundMinRTT are valid, check to see if delay
+ *increase triggers slow start exit.
+ *
+ * Depending on <h> HyStart++ algorithm state, returns 1 if the underlying
+ * congestion control algorithm may enter the Conservative Slow Start (CSS)
+ * state, 0 if not.
+ */
+static inline int quic_cc_hystart_may_enter_cs(struct quic_hystart *h)
+{
+ uint32_t rtt_thresh;
+
+ if (h->rtt_sample_count < HYSTART_N_RTT_SAMPLE ||
+ h->curr_rnd_min_rtt == UINT32_MAX || h->last_rnd_min_rtt == UINT32_MAX)
+ return 0;
+
+ rtt_thresh = QUIC_CLAMP(HYSTART_MIN_RTT_THRESH,
+ h->last_rnd_min_rtt / HYSTART_MIN_RTT_DIVISOR,
+ HYSTART_MAX_RTT_THRESH);
+ if (h->curr_rnd_min_rtt + rtt_thresh >= h->last_rnd_min_rtt) {
+ h->css_baseline_min_rtt = h->curr_rnd_min_rtt;
+ h->rtt_sample_count = 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* RFC 9406 4.2. Algorithm Details
+ * For CSS rounds where at least N_RTT_SAMPLE RTT samples have been obtained,
+ * check to see if the current round's minRTT drops below baseline (cssBaselineMinRtt)
+ * indicating that slow start exit was spurious.
+ *
+ * Return 1 if slow start exit was spurious, 0 if not. If the slow start
+ * exist was spurious, the caller must update the underlying congestion control
+ * algorithm to make it re-enter slow start state.
+ */
+static inline int quic_cc_hystart_may_reenter_ss(struct quic_hystart *h)
+{
+ if (h->rtt_sample_count < HYSTART_N_RTT_SAMPLE)
+ return 0;
+
+ h->css_rnd_count++;
+ h->rtt_sample_count = 0;
+
+ if (h->curr_rnd_min_rtt >= h->css_baseline_min_rtt) {
+ return 0;
+ }
+
+ h->css_baseline_min_rtt = UINT32_MAX;
+ return 1;
+}
diff --git a/include/haproxy/quic_conn-t.h b/include/haproxy/quic_conn-t.h
index 8aec6f0..a126e04 100644
--- a/include/haproxy/quic_conn-t.h
+++ b/include/haproxy/quic_conn-t.h
@@ -176,8 +176,15 @@ enum quic_pkt_type {
*/
#define QUIC_CONN_MAX_PACKET 64
-#define QUIC_STATELESS_RESET_PACKET_HEADER_LEN 5
-#define QUIC_STATELESS_RESET_PACKET_MINLEN (22 + QUIC_HAP_CID_LEN)
+/* RFC 9000 10.3. Stateless Reset
+ *
+ * To entities other than its intended recipient, a Stateless Reset will
+ * appear to be a packet with a short header. For the Stateless Reset to
+ * appear as a valid QUIC packet, the Unpredictable Bits field needs to
+ * include at least 38 bits of data (or 5 bytes, less the two fixed
+ * bits).
+ */
+#define QUIC_STATELESS_RESET_PACKET_MINLEN (5 + QUIC_STATELESS_RESET_TOKEN_LEN)
/* Similar to kernel min()/max() definitions. */
#define QUIC_MIN(a, b) ({ \
diff --git a/include/haproxy/quic_conn.h b/include/haproxy/quic_conn.h
index 92caed4..60bc407 100644
--- a/include/haproxy/quic_conn.h
+++ b/include/haproxy/quic_conn.h
@@ -138,7 +138,7 @@ static inline struct ncbuf *quic_get_ncbuf(struct ncbuf *ncbuf)
if (!ncb_is_null(ncbuf))
return ncbuf;
- b_alloc(&buf);
+ b_alloc(&buf, DB_MUX_RX);
BUG_ON(b_is_null(&buf));
*ncbuf = ncb_make(buf.area, buf.size, 0);
diff --git a/include/haproxy/quic_fctl-t.h b/include/haproxy/quic_fctl-t.h
new file mode 100644
index 0000000..9331619
--- /dev/null
+++ b/include/haproxy/quic_fctl-t.h
@@ -0,0 +1,15 @@
+#ifndef _HAPROXY_QUIC_FCTL_T_H
+#define _HAPROXY_QUIC_FCTL_T_H
+
+#include <stdint.h>
+
+struct quic_fctl {
+ /* Offset set by peer which must not be exceeded on send. */
+ uint64_t limit;
+ /* Offset which must never exceed limit. */
+ uint64_t off_real;
+ /* Offset which can go beyond limit one time before being blocked. */
+ uint64_t off_soft;
+};
+
+#endif /* _HAPROXY_QUIC_FCTL_T_H */
diff --git a/include/haproxy/quic_fctl.h b/include/haproxy/quic_fctl.h
new file mode 100644
index 0000000..8818372
--- /dev/null
+++ b/include/haproxy/quic_fctl.h
@@ -0,0 +1,19 @@
+#ifndef _HAPROXY_QUIC_FCTL_H
+#define _HAPROXY_QUIC_FCTL_H
+
+#include <haproxy/quic_fctl-t.h>
+
+void qfctl_init(struct quic_fctl *fctl, uint64_t limit);
+
+int qfctl_rblocked(const struct quic_fctl *fctl);
+int qfctl_sblocked(const struct quic_fctl *fctl);
+
+int qfctl_set_max(struct quic_fctl *fctl, uint64_t val,
+ int *unblock_soft, int *unblock_real);
+
+int qfctl_rinc(struct quic_fctl *fctl, uint64_t diff);
+int qfctl_sinc(struct quic_fctl *fctl, uint64_t diff);
+
+uint64_t qfctl_rcap(const struct quic_fctl *fctl);
+
+#endif /* _HAPROXY_QUIC_FCTL_H */
diff --git a/include/haproxy/quic_rx-t.h b/include/haproxy/quic_rx-t.h
index 9ef8e7a..6b5a0c4 100644
--- a/include/haproxy/quic_rx-t.h
+++ b/include/haproxy/quic_rx-t.h
@@ -5,6 +5,13 @@ extern struct pool_head *pool_head_quic_conn_rxbuf;
extern struct pool_head *pool_head_quic_dgram;
extern struct pool_head *pool_head_quic_rx_packet;
+#include <import/eb64tree.h>
+#include <haproxy/api-t.h>
+#include <haproxy/quic_cid-t.h>
+#include <inttypes.h>
+#include <sys/socket.h>
+
+struct quic_version;
/* Maximum number of ack-eliciting received packets since the last
* ACK frame was sent
*/
diff --git a/include/haproxy/quic_rx.h b/include/haproxy/quic_rx.h
index 494bc4a..3e65acb 100644
--- a/include/haproxy/quic_rx.h
+++ b/include/haproxy/quic_rx.h
@@ -30,8 +30,6 @@ int quic_dgram_parse(struct quic_dgram *dgram, struct quic_conn *from_qc,
int qc_treat_rx_pkts(struct quic_conn *qc);
int qc_parse_hd_form(struct quic_rx_packet *pkt,
unsigned char **pos, const unsigned char *end);
-int qc_treat_rx_crypto_frms(struct quic_conn *qc, struct quic_enc_level *el,
- struct ssl_sock_ctx *ctx);
int qc_handle_frms_of_lost_pkt(struct quic_conn *qc,
struct quic_tx_packet *pkt,
struct list *pktns_frm_list);
diff --git a/include/haproxy/quic_sock-t.h b/include/haproxy/quic_sock-t.h
index 67a5749..0b9c18c 100644
--- a/include/haproxy/quic_sock-t.h
+++ b/include/haproxy/quic_sock-t.h
@@ -36,8 +36,8 @@ struct quic_dgram {
struct sockaddr_storage daddr;
struct quic_conn *qc;
- struct list recv_list; /* elemt to quic_receiver_buf <dgram_list>. */
- struct mt_list handler_list; /* elem to quic_dghdlr <dgrams>. */
+ struct list recv_list; /* element pointing to quic_receiver_buf <dgram_list>. */
+ struct mt_list handler_list; /* element pointing to quic_dghdlr <dgrams>. */
};
/* QUIC datagram handler */
diff --git a/include/haproxy/quic_sock.h b/include/haproxy/quic_sock.h
index 531cf62..7236147 100644
--- a/include/haproxy/quic_sock.h
+++ b/include/haproxy/quic_sock.h
@@ -72,6 +72,14 @@ static inline char qc_test_fd(struct quic_conn *qc)
return qc->fd >= 0;
}
+/* Returns active socket for <qc> connection. This may be its owned connection
+ * socket or the listener one as a fallback.
+ */
+static inline int qc_fd(struct quic_conn *qc)
+{
+ return qc_test_fd(qc) ? qc->fd : qc->li->rx.fd;
+}
+
/* Try to increment <l> handshake current counter. If listener limit is
* reached, incrementation is rejected and 0 is returned.
*/
diff --git a/include/haproxy/quic_ssl.h b/include/haproxy/quic_ssl.h
index 8f7df47..a84f5ff 100644
--- a/include/haproxy/quic_ssl.h
+++ b/include/haproxy/quic_ssl.h
@@ -35,10 +35,6 @@
int ssl_quic_initial_ctx(struct bind_conf *bind_conf);
int qc_alloc_ssl_sock_ctx(struct quic_conn *qc);
-int qc_ssl_provide_quic_data(struct ncbuf *ncbuf,
- enum ssl_encryption_level_t level,
- struct ssl_sock_ctx *ctx,
- const unsigned char *data, size_t len);
int qc_ssl_provide_all_quic_data(struct quic_conn *qc, struct ssl_sock_ctx *ctx);
static inline void qc_free_ssl_sock_ctx(struct ssl_sock_ctx **ctx)
diff --git a/include/haproxy/quic_tls-t.h b/include/haproxy/quic_tls-t.h
index 326e01b..7f90d9a 100644
--- a/include/haproxy/quic_tls-t.h
+++ b/include/haproxy/quic_tls-t.h
@@ -21,6 +21,7 @@
#include <import/ebtree.h>
+#include <haproxy/buf-t.h>
#include <haproxy/ncbuf-t.h>
#include <haproxy/quic_ack-t.h>
#include <haproxy/openssl-compat.h>
@@ -237,10 +238,12 @@ struct quic_cstream {
struct quic_enc_level {
struct list list;
- /* Attach point to enqueue this encryption level during retransmissions */
- struct list retrans;
- /* pointer to list used only during retransmissions */
- struct list *retrans_frms;
+
+ /* Attach point to register encryption level before sending. */
+ struct list el_send;
+ /* Pointer to the frames used by sending functions */
+ struct list *send_frms;
+
/* Encryption level, as defined by the TLS stack. */
enum ssl_encryption_level_t level;
/* TLS encryption context (AEAD only) */
diff --git a/include/haproxy/quic_tls.h b/include/haproxy/quic_tls.h
index 86b8c1e..67c25aa 100644
--- a/include/haproxy/quic_tls.h
+++ b/include/haproxy/quic_tls.h
@@ -140,7 +140,15 @@ static inline const EVP_CIPHER *tls_aead(const SSL_CIPHER *cipher)
return EVP_aes_128_gcm();
case TLS1_3_CK_AES_256_GCM_SHA384:
return EVP_aes_256_gcm();
-#if !defined(OPENSSL_IS_AWSLC)
+#if !defined(OPENSSL_IS_AWSLC) && (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x4000000fL)
+ /* WT: LibreSSL has an issue with CHACHA20 running in-place till 3.9.2
+ * included, but the fix is already identified and will be merged
+ * into next major version. Given that on machines without AES-NI
+ * CHACHA20 is selected by default, this makes connections freeze
+ * on non-x86 machines, so we prefer to break them so that the
+ * client falls back to TCP. See GH issue #2569 for the context.
+ * Thanks to Theo Buehler for his help!
+ */
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
return EVP_chacha20_poly1305();
#endif
diff --git a/include/haproxy/quic_tx-t.h b/include/haproxy/quic_tx-t.h
index 4653f04..6979204 100644
--- a/include/haproxy/quic_tx-t.h
+++ b/include/haproxy/quic_tx-t.h
@@ -5,6 +5,9 @@
#define QUIC_DGRAM_HEADLEN (sizeof(uint16_t) + sizeof(void *))
#define QUIC_MAX_CC_BUFSIZE (2 * (QUIC_MIN_CC_PKTSIZE + QUIC_DGRAM_HEADLEN))
+#include <import/eb64tree.h>
+#include <haproxy/list-t.h>
+
extern struct pool_head *pool_head_quic_tx_packet;
extern struct pool_head *pool_head_quic_cc_buf;
diff --git a/include/haproxy/quic_tx.h b/include/haproxy/quic_tx.h
index 0659c14..55530d9 100644
--- a/include/haproxy/quic_tx.h
+++ b/include/haproxy/quic_tx.h
@@ -33,9 +33,11 @@ void qc_txb_release(struct quic_conn *qc);
int qc_purge_txbuf(struct quic_conn *qc, struct buffer *buf);
struct buffer *qc_get_txb(struct quic_conn *qc);
-int qc_prep_hpkts(struct quic_conn *qc, struct buffer *buf, struct list *qels);
-int qc_send_ppkts(struct buffer *buf, struct ssl_sock_ctx *ctx);
-int qc_send_app_pkts(struct quic_conn *qc, struct list *frms);
+void qel_register_send(struct list *send_list, struct quic_enc_level *qel,
+ struct list *frms);
+int qel_need_sending(struct quic_enc_level *qel, struct quic_conn *qc);
+int qc_send(struct quic_conn *qc, int old_data, struct list *send_list);
+
int qc_dgrams_retransmit(struct quic_conn *qc);
void qc_prep_hdshk_fast_retrans(struct quic_conn *qc,
struct list *ifrms, struct list *hfrms);
@@ -79,7 +81,7 @@ static inline void quic_tx_packet_refdec(struct quic_tx_packet *pkt)
}
/* Return the number of bytes which may be sent from <qc> connection when
- * it has not already been validated. Note that this is the responsability
+ * it has not already been validated. Note that this is the responsibility
* of the caller to check that the case with quic_peer_validated_addr().
* This latter BUG_ON() if 3 * qc->rx.bytes < qc->tx.prep_bytes.
*/
diff --git a/include/haproxy/receiver-t.h b/include/haproxy/receiver-t.h
index 0ae441e..90f52aa 100644
--- a/include/haproxy/receiver-t.h
+++ b/include/haproxy/receiver-t.h
@@ -37,6 +37,7 @@
#define RX_F_MWORKER 0x00000004 /* keep the FD open in the master but close it in the children */
#define RX_F_MUST_DUP 0x00000008 /* this receiver's fd must be dup() from a reference; ignore socket-level ops here */
#define RX_F_NON_SUSPENDABLE 0x00000010 /* this socket cannot be suspended hence must always be unbound */
+#define RX_F_PASS_PKTINFO 0x00000020 /* pass pktinfo in received messages */
/* Bit values for rx_settings->options */
#define RX_O_FOREIGN 0x00000001 /* receives on foreign addresses */
diff --git a/include/haproxy/resolvers-t.h b/include/haproxy/resolvers-t.h
index b727463..e10c6fa 100644
--- a/include/haproxy/resolvers-t.h
+++ b/include/haproxy/resolvers-t.h
@@ -134,7 +134,7 @@ struct resolv_response {
struct resolvers {
__decl_thread(HA_SPINLOCK_T lock);
unsigned int accepted_payload_size; /* maximum payload size we accept for responses */
- int nb_nameservers; /* total number of active nameservers in a resolvers section */
+ int nb_nameservers; /* total number of nameservers in a resolvers section */
int resolve_retries; /* number of retries before giving up */
struct { /* time to: */
int resolve; /* wait between 2 queries for the same resolution */
@@ -274,10 +274,7 @@ enum {
* OR provided IP found and preference is not match and an IP
* matching preference was found.
*/
- RSLV_UPD_CNAME, /* CNAME without any IP provided in the response */
- RSLV_UPD_NAME_ERROR, /* name in the response did not match the query */
RSLV_UPD_NO_IP_FOUND, /* no IP could be found in the response */
- RSLV_UPD_OBSOLETE_IP, /* The server IP was obsolete, and no other IP was found */
};
struct proxy;
diff --git a/include/haproxy/resolvers.h b/include/haproxy/resolvers.h
index 5d4c744..d4054b6 100644
--- a/include/haproxy/resolvers.h
+++ b/include/haproxy/resolvers.h
@@ -34,6 +34,7 @@ extern struct list sec_resolvers;
extern unsigned int resolv_failed_resolutions;
struct resolvers *find_resolvers_by_id(const char *id);
+struct dns_nameserver *find_nameserver_by_resolvers_and_id(struct resolvers *parent, unsigned int id);
struct resolv_srvrq *find_srvrq_by_name(const char *name, struct proxy *px);
struct resolv_srvrq *new_resolv_srvrq(struct server *srv, char *fqdn);
struct resolv_answer_item *find_srvrq_answer_record(const struct resolv_requester *requester);
@@ -60,7 +61,6 @@ int stats_dump_resolvers(struct stconn *sc,
struct list *stat_modules);
void resolv_stats_clear_counters(int clrall, struct list *stat_modules);
int resolv_allocate_counters(struct list *stat_modules);
-int dns_dgram_init(struct dns_nameserver *ns, struct sockaddr_storage *sk);
int resolvers_create_default();
#endif // _HAPROXY_RESOLVER_H
diff --git a/include/haproxy/ring-t.h b/include/haproxy/ring-t.h
index b89c886..4e091ee 100644
--- a/include/haproxy/ring-t.h
+++ b/include/haproxy/ring-t.h
@@ -27,7 +27,7 @@
#include <haproxy/thread.h>
/* The code below handles circular buffers with single-producer and multiple
- * readers (up to 255). The buffer storage area must remain always allocated.
+ * readers (up to 254). The buffer storage area must remain always allocated.
* It's made of series of payload blocks followed by a readers count (RC).
* There is always a readers count at the beginning of the buffer as well. Each
* payload block is composed of a varint-encoded size (VI) followed by the
@@ -96,11 +96,62 @@
#define RING_WF_WAIT_MODE 0x00000001 /* wait for new contents */
#define RING_WF_SEEK_NEW 0x00000002 /* seek to new contents */
+/* ring flags */
+#define RING_FL_MAPPED 0x00000001 /* mmapped area, must not free() */
+
+/* keep values below in decimal, they may be dumped in error messages */
+#define RING_WRITING_SIZE 255 /* the next message's size is being written */
+#define RING_MAX_READERS 254 /* highest supported value for RC */
+
+/* mask used to lock the tail */
+#define RING_TAIL_LOCK (1ULL << ((sizeof(size_t) * 8) - 1))
+
+/* A cell describing a waiting thread.
+ * ->next is initialized to 0x1 before the pointer is set, so that any
+ * leader thread can see that the pointer is not set yet. This allows
+ * to enqueue all waiting threads very quickly using XCHG() on the head
+ * without having to rely on a flaky CAS, while threads finish their setup
+ * in parallel. The pointer will turn to NULL again once the thread is
+ * released.
+ */
+struct ring_wait_cell {
+ size_t to_send_self; // size needed to serialize this msg
+ size_t needed_tot; // size needed to serialize pending msgs
+ size_t maxlen; // msg truncated to this size
+ const struct ist *pfx; // prefixes
+ size_t npfx; // #prefixes
+ const struct ist *msg; // message parts
+ size_t nmsg; // #message parts
+ struct ring_wait_cell *next; // next waiting thread
+};
+
+/* this is the mmapped part */
+struct ring_storage {
+ size_t size; // storage size
+ size_t rsvd; // header length (used for file-backed maps)
+ THREAD_PAD(64 - 2 * sizeof(size_t));
+ size_t tail; // storage tail
+ THREAD_PAD(64 - sizeof(size_t));
+ size_t head; // storage head
+ THREAD_PAD(64 - sizeof(size_t));
+ char area[0]; // storage area begins immediately here
+};
+
+/* this is the ring definition, config, waiters etc */
struct ring {
- struct buffer buf; // storage area
- struct list waiters; // list of waiters, for now, CLI "show event"
- __decl_thread(HA_RWLOCK_T lock);
+ struct ring_storage *storage; // the mapped part
+ struct mt_list waiters; // list of waiters, for now, CLI "show event"
int readers_count;
+ uint flags; // RING_FL_*
+ uint pending; // new writes that have not yet been subject to a wakeup
+ uint waking; // indicates a thread is currently waking up readers
+
+ /* keep the queue in a separate cache line below */
+ THREAD_PAD(64 - 3*sizeof(void*) - 4*sizeof(int));
+ struct {
+ struct ring_wait_cell *ptr;
+ THREAD_PAD(64 - sizeof(void*));
+ } queue[RING_WAIT_QUEUES + 1]; // wait queue + 1 spacer
};
#endif /* _HAPROXY_RING_T_H */
diff --git a/include/haproxy/ring.h b/include/haproxy/ring.h
index 71217d5..201ede4 100644
--- a/include/haproxy/ring.h
+++ b/include/haproxy/ring.h
@@ -25,13 +25,13 @@
#include <stdlib.h>
#include <import/ist.h>
#include <haproxy/ring-t.h>
+#include <haproxy/vecpair.h>
struct appctx;
struct ring *ring_new(size_t size);
-struct ring *ring_make_from_area(void *area, size_t size);
-struct ring *ring_cast_from_area(void *area);
-void ring_init(struct ring *ring, void* area, size_t size);
+struct ring *ring_make_from_area(void *area, size_t size, int reset);
+void ring_init(struct ring *ring, void *area, size_t size, int reset);
struct ring *ring_resize(struct ring *ring, size_t size);
void ring_free(struct ring *ring);
ssize_t ring_write(struct ring *ring, size_t maxlen, const struct ist pfx[], size_t npfx, const struct ist msg[], size_t nmsg);
@@ -42,6 +42,81 @@ int cli_io_handler_show_ring(struct appctx *appctx);
void cli_io_release_show_ring(struct appctx *appctx);
size_t ring_max_payload(const struct ring *ring);
+int ring_dispatch_messages(struct ring *ring, void *ctx, size_t *ofs_ptr, size_t *last_ofs_ptr, uint flags,
+ ssize_t (*msg_handler)(void *ctx, struct ist v1, struct ist v2, size_t ofs, size_t len));
+
+/* returns the ring storage's usable area */
+static inline void *ring_area(const struct ring *ring)
+{
+ return ring->storage->area;
+}
+
+/* returns the allocated area for the ring. It covers the whole
+ * area made of both the ring_storage and the usable area.
+ */
+static inline void *ring_allocated_area(const struct ring *ring)
+{
+ return ring->storage;
+}
+
+/* returns the number of bytes in the ring */
+static inline size_t ring_data(const struct ring *ring)
+{
+ size_t tail = HA_ATOMIC_LOAD(&ring->storage->tail) & ~RING_TAIL_LOCK;
+
+ return ((ring->storage->head <= tail) ?
+ 0 : ring->storage->size) + tail - ring->storage->head;
+}
+
+/* returns the usable size in bytes for the ring. It is smaller than
+ * the allocate size by the size of the ring_storage header.
+ */
+static inline size_t ring_size(const struct ring *ring)
+{
+ return ring->storage->size;
+}
+
+/* returns the allocated size in bytes for the ring. It covers the whole
+ * area made of both the ring_storage and the usable area.
+ */
+static inline size_t ring_allocated_size(const struct ring *ring)
+{
+ return ring->storage->size + ring->storage->rsvd;
+}
+
+/* returns the head offset of the ring */
+static inline size_t ring_head(const struct ring *ring)
+{
+ return ring->storage->head;
+}
+
+/* returns the ring's tail offset without the lock bit */
+static inline size_t ring_tail(const struct ring *ring)
+{
+ return HA_ATOMIC_LOAD(&ring->storage->tail) & ~RING_TAIL_LOCK;
+}
+
+/* duplicates ring <src> over ring <dst> for no more than <max> bytes or no
+ * more than the amount of data present in <src>. It's assumed that the
+ * destination ring is always large enough for <max>. The number of bytes
+ * copied (the min of src's size and max) is returned.
+ */
+static inline size_t ring_dup(struct ring *dst, const struct ring *src, size_t max)
+{
+ struct ist v1, v2;
+
+ vp_ring_to_data(&v1, &v2, ring_area(src), ring_size(src), ring_head(src), ring_tail(src));
+
+ if (max > ring_data(src))
+ max = ring_data(src);
+
+ BUG_ON(max > ring_size(dst));
+
+ vp_peek_ofs(v1, v2, 0, ring_area(dst), max);
+ dst->storage->head = 0;
+ dst->storage->tail = max;
+ return max;
+}
#endif /* _HAPROXY_RING_H */
diff --git a/include/haproxy/sample.h b/include/haproxy/sample.h
index 7e05e78..e8694c6 100644
--- a/include/haproxy/sample.h
+++ b/include/haproxy/sample.h
@@ -31,6 +31,7 @@
extern sample_cast_fct sample_casts[SMP_TYPES][SMP_TYPES];
extern const unsigned int fetch_cap[SMP_SRC_ENTRIES];
extern const char *smp_to_type[SMP_TYPES];
+int type_to_smp(const char *type);
struct sample_expr *sample_parse_expr(char **str, int *idx, const char *file, int line, char **err, struct arg_list *al, char **endptr);
int sample_parse_expr_cnv(char **str, int *idx, char **endptr, char **err_msg, struct arg_list *al, const char *file, int line,
diff --git a/include/haproxy/sc_strm.h b/include/haproxy/sc_strm.h
index 41f07e9..4eaef88 100644
--- a/include/haproxy/sc_strm.h
+++ b/include/haproxy/sc_strm.h
@@ -40,6 +40,12 @@ struct task *sc_conn_io_cb(struct task *t, void *ctx, unsigned int state);
int sc_conn_sync_recv(struct stconn *sc);
void sc_conn_sync_send(struct stconn *sc);
+int sc_applet_sync_recv(struct stconn *sc);
+void sc_applet_sync_send(struct stconn *sc);
+
+int sc_applet_sync_recv(struct stconn *sc);
+void sc_applet_sync_send(struct stconn *sc);
+
/* returns the channel which receives data from this stream connector (input channel) */
static inline struct channel *sc_ic(const struct stconn *sc)
@@ -149,8 +155,11 @@ static inline int sc_alloc_ibuf(struct stconn *sc, struct buffer_wait *wait)
int ret;
ret = channel_alloc_buffer(sc_ic(sc), wait);
- if (!ret)
+ if (ret)
+ sc_used_buff(sc);
+ else
sc_need_buff(sc);
+
return ret;
}
@@ -319,6 +328,30 @@ static inline void sc_chk_snd(struct stconn *sc)
sc->app_ops->chk_snd(sc);
}
+
+/* Perform a synchronous receive using the right version, depending the endpoing
+ * is a connection or an applet.
+ */
+static inline int sc_sync_recv(struct stconn *sc)
+{
+ if (sc_ep_test(sc, SE_FL_T_MUX))
+ return sc_conn_sync_recv(sc);
+ else if (sc_ep_test(sc, SE_FL_T_APPLET))
+ return sc_applet_sync_recv(sc);
+ return 0;
+}
+
+/* Perform a synchronous send using the right version, depending the endpoing is
+ * a connection or an applet.
+ */
+static inline void sc_sync_send(struct stconn *sc)
+{
+ if (sc_ep_test(sc, SE_FL_T_MUX))
+ sc_conn_sync_send(sc);
+ else if (sc_ep_test(sc, SE_FL_T_APPLET))
+ sc_applet_sync_send(sc);
+}
+
/* Combines both sc_update_rx() and sc_update_tx() at once */
static inline void sc_update(struct stconn *sc)
{
diff --git a/include/haproxy/server-t.h b/include/haproxy/server-t.h
index 666d2cc..af58a56 100644
--- a/include/haproxy/server-t.h
+++ b/include/haproxy/server-t.h
@@ -31,7 +31,7 @@
#include <haproxy/check-t.h>
#include <haproxy/connection-t.h>
#include <haproxy/counters-t.h>
-#include <haproxy/freq_ctr-t.h>
+#include <haproxy/guid-t.h>
#include <haproxy/listener-t.h>
#include <haproxy/obj_type-t.h>
#include <haproxy/queue-t.h>
@@ -41,6 +41,7 @@
#include <haproxy/task-t.h>
#include <haproxy/thread-t.h>
#include <haproxy/event_hdl-t.h>
+#include <haproxy/log-t.h>
#include <haproxy/tools-t.h>
@@ -223,6 +224,13 @@ struct pid_list {
int exited;
};
+/* srv methods of computing chash keys */
+enum srv_hash_key {
+ SRV_HASH_KEY_ID = 0, /* derived from server puid */
+ SRV_HASH_KEY_ADDR, /* derived from server address */
+ SRV_HASH_KEY_ADDR_PORT /* derived from server address and port */
+};
+
/* A tree occurrence is a descriptor of a place in a tree, with a pointer back
* to the server itself.
*/
@@ -262,7 +270,7 @@ enum __attribute__((__packed__)) srv_ws_mode {
*/
struct srv_pp_tlv_list {
struct list list;
- struct list fmt;
+ struct lf_expr fmt;
char *fmt_string;
unsigned char type;
};
@@ -293,6 +301,8 @@ struct server {
struct srv_per_tgroup *per_tgrp; /* array of per-tgroup stuff such as idle conns */
unsigned int *curr_idle_thr; /* Current number of orphan idling connections per thread */
+ char *pool_conn_name;
+ struct sample_expr *pool_conn_name_expr;
unsigned int pool_purge_delay; /* Delay before starting to purge the idle conns pool */
unsigned int low_idle_conns; /* min idle connection count to start picking from other threads */
unsigned int max_idle_conns; /* Max number of connection allowed in the orphan connections list */
@@ -338,6 +348,7 @@ struct server {
unsigned int est_need_conns; /* Estimate on the number of needed connections (max of curr and previous max_used) */
struct queue queue; /* pending connections */
+ struct mt_list sess_conns; /* list of private conns managed by a session on this server */
/* Element below are usd by LB algorithms and must be doable in
* parallel to other threads reusing connections above.
@@ -356,7 +367,6 @@ struct server {
int cur_sess; /* number of currently active sessions (including syn_sent) */
int served; /* # of active sessions currently being served (ie not pending) */
int consecutive_errors; /* current number of consecutive errors */
- struct freq_ctr sess_per_sec; /* sessions per second on this server */
struct be_counters counters; /* statistics counters */
/* Below are some relatively stable settings, only changed under the lock */
@@ -366,12 +376,13 @@ struct server {
struct tree_occ *lb_nodes; /* lb_nodes_tot * struct tree_occ */
unsigned lb_nodes_tot; /* number of allocated lb_nodes (C-HASH) */
unsigned lb_nodes_now; /* number of lb_nodes placed in the tree (C-HASH) */
+ enum srv_hash_key hash_key; /* method to compute node hash (C-HASH) */
+ unsigned lb_server_key; /* hash of the values indicated by "hash_key" (C-HASH) */
const struct netns_entry *netns; /* contains network namespace name or NULL. Network namespace comes from configuration */
struct xprt_ops *xprt; /* transport-layer operations */
unsigned int svc_port; /* the port to connect to (for relevant families) */
unsigned down_time; /* total time the server was down */
- time_t last_change; /* last time, when the state was changed */
int puid; /* proxy-unique server ID, used for SNMP, and "first" LB algo */
int tcp_ut; /* for TCP, user timeout */
@@ -455,6 +466,8 @@ struct server {
event_hdl_sub_list e_subs; /* event_hdl: server's subscribers list (atomically updated) */
+ struct guid_node guid; /* GUID global tree node */
+
/* warning, these structs are huge, keep them at the bottom */
struct conn_src conn_src; /* connection source settings */
struct sockaddr_storage addr; /* the address to connect to, doesn't include the port */
@@ -608,6 +621,56 @@ struct server_inetaddr {
} port;
};
+/* struct to store information about server's addr / port updater in
+ * INET context
+ */
+enum server_inetaddr_updater_by {
+ SERVER_INETADDR_UPDATER_BY_NONE = 0,
+ SERVER_INETADDR_UPDATER_BY_CLI,
+ SERVER_INETADDR_UPDATER_BY_LUA,
+ SERVER_INETADDR_UPDATER_BY_DNS_AR,
+ SERVER_INETADDR_UPDATER_BY_DNS_CACHE,
+ SERVER_INETADDR_UPDATER_BY_DNS_RESOLVER,
+ /* changes here must be reflected in SERVER_INETADDR_UPDATER_*
+ * helper macros and in server_inetaddr_updater_by_to_str() func
+ */
+};
+struct server_inetaddr_updater {
+ enum server_inetaddr_updater_by by; // by identifier (unique)
+ uint8_t dns; // is dns involved?
+ union {
+ struct {
+ unsigned int ns_id; // nameserver id responsible for the update
+ } dns_resolver; // SERVER_INETADDR_UPDATER_DNS_RESOLVER specific infos
+ } u; // per updater's additional ctx
+};
+#define SERVER_INETADDR_UPDATER_NONE \
+ (struct server_inetaddr_updater){ .by = SERVER_INETADDR_UPDATER_BY_NONE, \
+ .dns = 0 }
+
+#define SERVER_INETADDR_UPDATER_CLI \
+ (struct server_inetaddr_updater){ .by = SERVER_INETADDR_UPDATER_BY_CLI, \
+ .dns = 0 }
+
+#define SERVER_INETADDR_UPDATER_LUA \
+ (struct server_inetaddr_updater){ .by = SERVER_INETADDR_UPDATER_BY_LUA, \
+ .dns = 0 }
+
+#define SERVER_INETADDR_UPDATER_DNS_AR \
+ (struct server_inetaddr_updater){ .by = SERVER_INETADDR_UPDATER_BY_DNS_AR, \
+ .dns = 1 }
+
+#define SERVER_INETADDR_UPDATER_DNS_CACHE \
+ (struct server_inetaddr_updater){ .by = SERVER_INETADDR_UPDATER_BY_DNS_CACHE, \
+ .dns = 1 }
+
+#define SERVER_INETADDR_UPDATER_DNS_RESOLVER(_ns_id) \
+ (struct server_inetaddr_updater){ \
+ .by = SERVER_INETADDR_UPDATER_BY_DNS_RESOLVER, \
+ .dns = 1, \
+ .u.dns_resolver.ns_id = _ns_id, \
+ }
+
/* data provided to EVENT_HDL_SUB_SERVER_INETADDR handlers through
* event_hdl facility
*
@@ -622,7 +685,7 @@ struct event_hdl_cb_data_server_inetaddr {
struct {
struct server_inetaddr prev;
struct server_inetaddr next;
- uint8_t purge_conn; /* set to 1 if the network change will force a connection cleanup */
+ struct server_inetaddr_updater updater;
} safe;
/* no unsafe data */
};
diff --git a/include/haproxy/server.h b/include/haproxy/server.h
index 2ba6e45..b8f8c71 100644
--- a/include/haproxy/server.h
+++ b/include/haproxy/server.h
@@ -26,9 +26,11 @@
#include <haproxy/api.h>
#include <haproxy/applet-t.h>
+#include <haproxy/arg-t.h>
#include <haproxy/freq_ctr.h>
#include <haproxy/proxy-t.h>
#include <haproxy/resolvers-t.h>
+#include <haproxy/sample-t.h>
#include <haproxy/server-t.h>
#include <haproxy/task.h>
#include <haproxy/thread-t.h>
@@ -43,17 +45,24 @@ extern struct list servers_list;
extern struct dict server_key_dict;
int srv_downtime(const struct server *s);
-int srv_lastsession(const struct server *s);
int srv_getinter(const struct check *check);
+void srv_settings_init(struct server *srv);
void srv_settings_cpy(struct server *srv, const struct server *src, int srv_tmpl);
int parse_server(const char *file, int linenum, char **args, struct proxy *curproxy, const struct proxy *defproxy, int parse_flags);
-int srv_update_addr(struct server *s, void *ip, int ip_sin_family, const char *updater);
-int server_parse_sni_expr(struct server *newsrv, struct proxy *px, char **err);
-const char *srv_update_addr_port(struct server *s, const char *addr, const char *port, char *updater);
+int srv_update_addr(struct server *s, void *ip, int ip_sin_family, struct server_inetaddr_updater updater);
+struct sample_expr *_parse_srv_expr(char *expr, struct arg_list *args_px,
+ const char *file, int linenum, char **err);
+int server_set_inetaddr(struct server *s, const struct server_inetaddr *inetaddr, struct server_inetaddr_updater updater, struct buffer *msg);
+int server_set_inetaddr_warn(struct server *s, const struct server_inetaddr *inetaddr, struct server_inetaddr_updater updater);
+void server_get_inetaddr(struct server *s, struct server_inetaddr *inetaddr);
+const char *srv_update_addr_port(struct server *s, const char *addr, const char *port, struct server_inetaddr_updater updater);
+const char *server_inetaddr_updater_by_to_str(enum server_inetaddr_updater_by by);
const char *srv_update_check_addr_port(struct server *s, const char *addr, const char *port);
const char *srv_update_agent_addr_port(struct server *s, const char *addr, const char *port);
struct server *server_find_by_id(struct proxy *bk, int id);
+struct server *server_find_by_id_unique(struct proxy *bk, int id, uint32_t rid);
struct server *server_find_by_name(struct proxy *bk, const char *name);
+struct server *server_find_by_name_unique(struct proxy *bk, const char *name, uint32_t rid);
struct server *server_find_best_match(struct proxy *bk, char *name, int id, int *diff);
void apply_server_state(void);
void srv_compute_all_admin_states(struct proxy *px);
@@ -69,11 +78,11 @@ void srv_set_ssl(struct server *s, int use_ssl);
const char *srv_adm_st_chg_cause(enum srv_adm_st_chg_cause cause);
const char *srv_op_st_chg_cause(enum srv_op_st_chg_cause cause);
void srv_event_hdl_publish_check(struct server *srv, struct check *check);
+int srv_check_for_deletion(const char *bename, const char *svname, struct proxy **pb, struct server **ps, const char **pm);
/* functions related to server name resolution */
int srv_prepare_for_resolution(struct server *srv, const char *hostname);
-int srvrq_update_srv_status(struct server *s, int has_no_ip);
-int snr_update_srv_status(struct server *s, int has_no_ip);
+int srvrq_set_srv_down(struct server *s);
int srv_set_fqdn(struct server *srv, const char *fqdn, int resolv_locked);
const char *srv_update_fqdn(struct server *server, const char *fqdn, const char *updater, int dns_locked);
int snr_resolution_cb(struct resolv_requester *requester, struct dns_counters *counters);
@@ -118,14 +127,6 @@ const char *server_parse_weight_change_request(struct server *sv,
const char *weight_str);
/*
- * Parses addr_str and configures sv accordingly. updater precise
- * the source of the change in the associated message log.
- * Returns NULL on success, error message string otherwise.
- */
-const char *server_parse_addr_change_request(struct server *sv,
- const char *addr_str, const char *updater);
-
-/*
* Parses maxconn_str and configures sv accordingly.
* Returns NULL on success, error message string otherwise.
*/
@@ -177,12 +178,12 @@ void srv_set_dyncookie(struct server *s);
int srv_check_reuse_ws(struct server *srv);
const struct mux_ops *srv_get_ws_proto(struct server *srv);
-/* increase the number of cumulated connections on the designated server */
+/* increase the number of cumulated streams on the designated server */
static inline void srv_inc_sess_ctr(struct server *s)
{
_HA_ATOMIC_INC(&s->counters.cum_sess);
HA_ATOMIC_UPDATE_MAX(&s->counters.sps_max,
- update_freq_ctr(&s->sess_per_sec, 1));
+ update_freq_ctr(&s->counters.sess_per_sec, 1));
}
/* set the time of last session on the designated server */
diff --git a/include/haproxy/session-t.h b/include/haproxy/session-t.h
index dff167e..7f034dd 100644
--- a/include/haproxy/session-t.h
+++ b/include/haproxy/session-t.h
@@ -39,6 +39,7 @@
enum {
SESS_FL_NONE = 0x00000000, /* nothing */
SESS_FL_PREFER_LAST = 0x00000001, /* NTML authent, we should reuse last conn */
+ SESS_FL_RELEASE_LI = 0x00000002, /* session responsible to decrement listener counters on release */
};
/* max number of idle server connections kept attached to a session */
@@ -57,15 +58,23 @@ struct session {
long t_idle; /* idle duration, -1 if never occurs */
int idle_conns; /* Number of connections we're currently responsible for that we are not using */
unsigned int flags; /* session flags, SESS_FL_* */
- struct list srv_list; /* List of servers and the connections the session is currently responsible for */
+ struct list priv_conns; /* list of private conns */
struct sockaddr_storage *src; /* source address (pool), when known, otherwise NULL */
struct sockaddr_storage *dst; /* destination address (pool), when known, otherwise NULL */
};
-struct sess_srv_list {
- void *target;
+/*
+ * List of private conns managed by a session, indexed by server
+ * Stored both into the session and server instances
+ */
+struct sess_priv_conns {
+ void *target; /* Server or dispatch used for indexing */
struct list conn_list; /* Head of the connections list */
- struct list srv_list; /* Next element of the server list */
+
+ struct list sess_el; /* Element of session.priv_conns */
+ struct mt_list srv_el; /* Element of server.sess_conns */
+
+ int tid;
};
#endif /* _HAPROXY_SESSION_T_H */
diff --git a/include/haproxy/session.h b/include/haproxy/session.h
index 8a62805..48d2fa0 100644
--- a/include/haproxy/session.h
+++ b/include/haproxy/session.h
@@ -32,13 +32,16 @@
#include <haproxy/stick_table.h>
extern struct pool_head *pool_head_session;
-extern struct pool_head *pool_head_sess_srv_list;
+extern struct pool_head *pool_head_sess_priv_conns;
struct session *session_new(struct proxy *fe, struct listener *li, enum obj_type *origin);
void session_free(struct session *sess);
+void conn_session_free(struct connection *conn);
int session_accept_fd(struct connection *cli_conn);
int conn_complete_session(struct connection *conn);
struct task *session_expire_embryonic(struct task *t, void *context, unsigned int state);
+void __session_add_glitch_ctr(struct session *sess, uint inc);
+
/* Remove the refcount from the session to the tracked counters, and clear the
* pointer to ensure this is only performed once. The caller is responsible for
@@ -123,11 +126,21 @@ static inline void session_inc_http_fail_ctr(struct session *sess)
stkctr_inc_http_fail_ctr(&sess->stkctr[i]);
}
+/* Add <inc> to the number of cumulated glitches in the tracked counters, and
+ * implicitly update the rate if also tracked.
+ */
+static inline void session_add_glitch_ctr(struct session *sess, uint inc)
+{
+ if (sess->stkctr && inc)
+ __session_add_glitch_ctr(sess, inc);
+}
-/* Remove the connection from the session list, and destroy the srv_list if it's now empty */
+/* Remove the connection from the session list, and destroy sess_priv_conns
+ * element if it's now empty.
+ */
static inline void session_unown_conn(struct session *sess, struct connection *conn)
{
- struct sess_srv_list *srv_list = NULL;
+ struct sess_priv_conns *pconns = NULL;
BUG_ON(objt_listener(conn->target));
@@ -138,58 +151,66 @@ static inline void session_unown_conn(struct session *sess, struct connection *c
* conn->owner that points to a dead session, but in this case the
* element is not linked.
*/
- if (!LIST_INLIST(&conn->session_list))
+ if (!LIST_INLIST(&conn->sess_el))
return;
if (conn->flags & CO_FL_SESS_IDLE)
sess->idle_conns--;
- LIST_DEL_INIT(&conn->session_list);
+ LIST_DEL_INIT(&conn->sess_el);
conn->owner = NULL;
- list_for_each_entry(srv_list, &sess->srv_list, srv_list) {
- if (srv_list->target == conn->target) {
- if (LIST_ISEMPTY(&srv_list->conn_list)) {
- LIST_DELETE(&srv_list->srv_list);
- pool_free(pool_head_sess_srv_list, srv_list);
+ list_for_each_entry(pconns, &sess->priv_conns, sess_el) {
+ if (pconns->target == conn->target) {
+ if (LIST_ISEMPTY(&pconns->conn_list)) {
+ LIST_DELETE(&pconns->sess_el);
+ MT_LIST_DELETE(&pconns->srv_el);
+ pool_free(pool_head_sess_priv_conns, pconns);
}
break;
}
}
}
-/* Add the connection <conn> to the server list of the session <sess>. This
- * function is called only if the connection is private. Nothing is performed if
- * the connection is already in the session sever list or if the session does
- * not own the connection.
+/* Add the connection <conn> to the private conns list of session <sess>. This
+ * function is called only if the connection is private. Nothing is performed
+ * if the connection is already in the session list or if the session does not
+ * owned the connection.
*/
static inline int session_add_conn(struct session *sess, struct connection *conn, void *target)
{
- struct sess_srv_list *srv_list = NULL;
+ struct sess_priv_conns *pconns = NULL;
+ struct server *srv = objt_server(conn->target);
int found = 0;
BUG_ON(objt_listener(conn->target));
/* Already attach to the session or not the connection owner */
- if (!LIST_ISEMPTY(&conn->session_list) || (conn->owner && conn->owner != sess))
+ if (!LIST_ISEMPTY(&conn->sess_el) || (conn->owner && conn->owner != sess))
return 1;
- list_for_each_entry(srv_list, &sess->srv_list, srv_list) {
- if (srv_list->target == target) {
+ list_for_each_entry(pconns, &sess->priv_conns, sess_el) {
+ if (pconns->target == target) {
found = 1;
break;
}
}
if (!found) {
/* The session has no connection for the server, create a new entry */
- srv_list = pool_alloc(pool_head_sess_srv_list);
- if (!srv_list)
+ pconns = pool_alloc(pool_head_sess_priv_conns);
+ if (!pconns)
return 0;
- srv_list->target = target;
- LIST_INIT(&srv_list->conn_list);
- LIST_APPEND(&sess->srv_list, &srv_list->srv_list);
+ pconns->target = target;
+ LIST_INIT(&pconns->conn_list);
+ LIST_APPEND(&sess->priv_conns, &pconns->sess_el);
+
+ MT_LIST_INIT(&pconns->srv_el);
+ if (srv)
+ MT_LIST_APPEND(&srv->sess_conns, &pconns->srv_el);
+
+ pconns->tid = tid;
}
- LIST_APPEND(&srv_list->conn_list, &conn->session_list);
+ LIST_APPEND(&pconns->conn_list, &conn->sess_el);
- /* Ensure owner is set for connection. It could have been resetted
+ /* Ensure owner is set for connection. It could have been reset
* prior on after a session_add_conn() failure.
*/
conn->owner = sess;
@@ -226,11 +247,11 @@ static inline int session_check_idle_conn(struct session *sess, struct connectio
static inline struct connection *session_get_conn(struct session *sess, void *target, int64_t hash)
{
struct connection *srv_conn = NULL;
- struct sess_srv_list *srv_list;
+ struct sess_priv_conns *pconns;
- list_for_each_entry(srv_list, &sess->srv_list, srv_list) {
- if (srv_list->target == target) {
- list_for_each_entry(srv_conn, &srv_list->conn_list, session_list) {
+ list_for_each_entry(pconns, &sess->priv_conns, sess_el) {
+ if (pconns->target == target) {
+ list_for_each_entry(srv_conn, &pconns->conn_list, sess_el) {
if ((srv_conn->hash_node && srv_conn->hash_node->node.key == hash) &&
srv_conn->mux &&
(srv_conn->mux->avail_streams(srv_conn) > 0) &&
diff --git a/include/haproxy/shctx.h b/include/haproxy/shctx.h
index a57cf15..01bb09d 100644
--- a/include/haproxy/shctx.h
+++ b/include/haproxy/shctx.h
@@ -21,7 +21,7 @@
int shctx_init(struct shared_context **orig_shctx,
int maxblocks, int blocksize, unsigned int maxobjsz,
- int extra);
+ int extra, __maybe_unused const char *name);
struct shared_block *shctx_row_reserve_hot(struct shared_context *shctx,
struct shared_block *last, int data_len);
void shctx_row_detach(struct shared_context *shctx, struct shared_block *first);
diff --git a/include/haproxy/sink-t.h b/include/haproxy/sink-t.h
index 79a0dda..d5e1cec 100644
--- a/include/haproxy/sink-t.h
+++ b/include/haproxy/sink-t.h
@@ -60,9 +60,8 @@ struct sink {
struct sig_handler *forward_sighandler; /* signal handler */
struct {
struct ring *ring; // used by ring buffer and STRM sender
- unsigned int dropped; // dropped events since last one.
+ unsigned int dropped; // 2*dropped events since last one + 1 for purge in progress.
int fd; // fd num for FD type sink
- __decl_thread(HA_RWLOCK_T lock); // shared/excl for dropped
} ctx;
};
diff --git a/include/haproxy/sink.h b/include/haproxy/sink.h
index 3b428a1..bdc8447 100644
--- a/include/haproxy/sink.h
+++ b/include/haproxy/sink.h
@@ -52,18 +52,10 @@ int sink_announce_dropped(struct sink *sink, struct log_header hdr);
static inline ssize_t sink_write(struct sink *sink, struct log_header hdr,
size_t maxlen, const struct ist msg[], size_t nmsg)
{
- ssize_t sent;
+ ssize_t sent = 0;
- if (unlikely(sink->ctx.dropped > 0)) {
- /* We need to take an exclusive lock so that other producers
- * don't do the same thing at the same time and above all we
- * want to be sure others have finished sending their messages
- * so that the dropped event arrives exactly at the right
- * position.
- */
- HA_RWLOCK_WRLOCK(RING_LOCK, &sink->ctx.lock);
+ if (unlikely(HA_ATOMIC_LOAD(&sink->ctx.dropped) > 0)) {
sent = sink_announce_dropped(sink, hdr);
- HA_RWLOCK_WRUNLOCK(RING_LOCK, &sink->ctx.lock);
if (!sent) {
/* we failed, we don't try to send our log as if it
@@ -73,13 +65,11 @@ static inline ssize_t sink_write(struct sink *sink, struct log_header hdr,
}
}
- HA_RWLOCK_RDLOCK(RING_LOCK, &sink->ctx.lock);
sent = __sink_write(sink, hdr, maxlen, msg, nmsg);
- HA_RWLOCK_RDUNLOCK(RING_LOCK, &sink->ctx.lock);
fail:
if (unlikely(sent <= 0))
- HA_ATOMIC_INC(&sink->ctx.dropped);
+ HA_ATOMIC_ADD(&sink->ctx.dropped, 2);
return sent;
}
diff --git a/include/haproxy/sock.h b/include/haproxy/sock.h
index 60e81ec..017e0ad 100644
--- a/include/haproxy/sock.h
+++ b/include/haproxy/sock.h
@@ -30,7 +30,7 @@
#include <haproxy/listener-t.h>
#include <haproxy/sock-t.h>
-int sock_create_server_socket(struct connection *conn);
+int sock_create_server_socket(struct connection *conn, struct proxy *be, int *stream_err);
void sock_enable(struct receiver *rx);
void sock_disable(struct receiver *rx);
void sock_unbind(struct receiver *rx);
@@ -51,6 +51,39 @@ int sock_check_events(struct connection *conn, int event_type);
void sock_ignore_events(struct connection *conn, int event_type);
int _sock_supports_reuseport(const struct proto_fam *fam, int type, int protocol);
+/* Sets tos sockopt on socket depending on addr target family */
+static inline void sock_set_tos(int fd, struct sockaddr_storage *addr, int tos)
+{
+#ifdef IP_TOS
+ if (addr->ss_family == AF_INET)
+ setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
+#endif
+#ifdef IPV6_TCLASS
+ if (addr->ss_family == AF_INET6) {
+ if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)addr)->sin6_addr))
+ /* v4-mapped addresses need IP_TOS */
+ setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
+ else
+ setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos));
+ }
+#endif
+}
+
+/* Sets mark sockopt on socket */
+static inline void sock_set_mark(int fd, sa_family_t sock_family, int mark)
+{
+ if ((sock_family == AF_INET) || (sock_family == AF_INET6)) {
+#if defined(SO_MARK)
+ setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(mark));
+/* FreeBSD */
+#elif defined(SO_USER_COOKIE)
+ setsockopt(fd, SOL_SOCKET, SO_USER_COOKIE, &mark, sizeof(mark));
+/* OpenBSD */
+#elif defined(SO_RTABLE)
+ setsockopt(fd, SOL_SOCKET, SO_RTABLE, &mark, sizeof(mark));
+#endif
+ }
+}
#endif /* _HAPROXY_SOCK_H */
diff --git a/include/haproxy/ssl_ckch-t.h b/include/haproxy/ssl_ckch-t.h
index 0002b84..0e501e5 100644
--- a/include/haproxy/ssl_ckch-t.h
+++ b/include/haproxy/ssl_ckch-t.h
@@ -55,6 +55,16 @@ struct ckch_data {
struct buffer *ocsp_response;
X509 *ocsp_issuer;
OCSP_CERTID *ocsp_cid;
+};
+
+/* configuration for the ckch_store */
+struct ckch_conf {
+ int used;
+ char *crt;
+ char *key;
+ char *ocsp;
+ char *issuer;
+ char *sctl;
int ocsp_update_mode;
};
@@ -71,6 +81,7 @@ struct ckch_store {
struct ckch_data *data;
struct list ckch_inst; /* list of ckch_inst which uses this ckch_node */
struct list crtlist_entry; /* list of entries which use this store */
+ struct ckch_conf conf;
struct ebmb_node node;
char path[VAR_ARRAY];
};
@@ -150,6 +161,16 @@ enum {
CERT_TYPE_MAX,
};
+/*
+ * When crt-store options are set from a crt-list, the crt-store options must be explicit everywhere.
+ * When crt-store options are set from a crt-store, the crt-store options can be empty, or the exact same
+ */
+enum {
+ CKCH_CONF_SET_EMPTY = 0, /* config is empty */
+ CKCH_CONF_SET_CRTLIST = 1, /* config is set from a crt-list */
+ CKCH_CONF_SET_CRTSTORE = 2, /* config is defined in a crt-store */
+};
+
struct cert_exts {
const char *ext;
int type;
@@ -157,5 +178,23 @@ struct cert_exts {
/* add a parsing callback */
};
+/* argument types */
+enum parse_type_t {
+ PARSE_TYPE_NONE = 0,
+ PARSE_TYPE_INT,
+ PARSE_TYPE_STR, /* string which is strdup() */
+ PARSE_TYPE_ONOFF, /* "on" or "off" keyword */
+};
+
+struct ckch_conf_kws {
+ const char *name;
+ ssize_t offset;
+ enum parse_type_t type;
+ int (*func)(void *value, char *buf, struct ckch_data *d, int cli, char **err);
+ char **base; /* ptr to the base path */
+};
+
+extern struct ckch_conf_kws ckch_conf_kws[];
+
#endif /* USE_OPENSSL */
#endif /* _HAPROXY_SSL_CKCH_T_H */
diff --git a/include/haproxy/ssl_ckch.h b/include/haproxy/ssl_ckch.h
index 64ac3df..e635663 100644
--- a/include/haproxy/ssl_ckch.h
+++ b/include/haproxy/ssl_ckch.h
@@ -37,18 +37,27 @@ int ssl_sock_load_sctl_from_file(const char *sctl_path, char *buf, struct ckch_d
int ssl_sock_load_issuer_file_into_ckch(const char *path, char *buf, struct ckch_data *data, char **err);
/* ckch_store functions */
-struct ckch_store *ckchs_load_cert_file(char *path, char **err);
+struct ckch_store *ckch_store_new_load_files_path(char *path, char **err);
+struct ckch_store *ckch_store_new_load_files_conf(char *name, struct ckch_conf *conf, char **err);
struct ckch_store *ckchs_lookup(char *path);
struct ckch_store *ckchs_dup(const struct ckch_store *src);
struct ckch_store *ckch_store_new(const char *filename);
void ckch_store_free(struct ckch_store *store);
void ckch_store_replace(struct ckch_store *old_ckchs, struct ckch_store *new_ckchs);
+int ckch_store_load_files(struct ckch_conf *f, struct ckch_store *c, int cli, char **err);
+
+/* ckch_conf functions */
+
+int ckch_conf_parse(char **args, int cur_arg, struct ckch_conf *f, int *found, const char *file, int linenum, char **err);
+void ckch_conf_clean(struct ckch_conf *conf);
+int ckch_conf_cmp(struct ckch_conf *conf1, struct ckch_conf *conf2, char **err);
+int ckch_conf_cmp_empty(struct ckch_conf *prev, char **err);
/* ckch_inst functions */
void ckch_inst_free(struct ckch_inst *inst);
struct ckch_inst *ckch_inst_new();
int ckch_inst_new_load_store(const char *path, struct ckch_store *ckchs, struct bind_conf *bind_conf,
- struct ssl_bind_conf *ssl_conf, char **sni_filter, int fcount, struct ckch_inst **ckchi, char **err);
+ struct ssl_bind_conf *ssl_conf, char **sni_filter, int fcount, int is_default, struct ckch_inst **ckchi, char **err);
int ckch_inst_new_load_srv_store(const char *path, struct ckch_store *ckchs,
struct ckch_inst **ckchi, char **err);
int ckch_inst_rebuild(struct ckch_store *ckch_store, struct ckch_inst *ckchi,
@@ -70,6 +79,14 @@ int ssl_store_load_locations_file(char *path, int create_if_none, enum cafile_ty
int __ssl_store_load_locations_file(char *path, int create_if_none, enum cafile_type type, int shuterror);
extern struct cert_exts cert_exts[];
+extern int (*ssl_commit_crlfile_cb)(const char *path, X509_STORE *ctx, char **err);
+
+/* ckch_conf keyword loading */
+static inline int ckch_conf_load_pem(void *value, char *buf, struct ckch_data *d, int cli, char **err) { if (cli) return 0; return ssl_sock_load_pem_into_ckch(value, buf, d, err); }
+static inline int ckch_conf_load_key(void *value, char *buf, struct ckch_data *d, int cli, char **err) { if (cli) return 0; return ssl_sock_load_key_into_ckch(value, buf, d, err); }
+static inline int ckch_conf_load_ocsp_response(void *value, char *buf, struct ckch_data *d, int cli, char **err) { if (cli) return 0; return ssl_sock_load_ocsp_response_from_file(value, buf, d, err); }
+static inline int ckch_conf_load_ocsp_issuer(void *value, char *buf, struct ckch_data *d, int cli, char **err) { if (cli) return 0; return ssl_sock_load_issuer_file_into_ckch(value, buf, d, err); }
+static inline int ckch_conf_load_sctl(void *value, char *buf, struct ckch_data *d, int cli, char **err) { if (cli) return 0; return ssl_sock_load_sctl_from_file(value, buf, d, err); }
#endif /* USE_OPENSSL */
#endif /* _HAPROXY_SSL_CRTLIST_H */
diff --git a/include/haproxy/ssl_crtlist.h b/include/haproxy/ssl_crtlist.h
index 961cfc3..f81ee9e 100644
--- a/include/haproxy/ssl_crtlist.h
+++ b/include/haproxy/ssl_crtlist.h
@@ -38,7 +38,7 @@ void crtlist_free(struct crtlist *crtlist);
struct crtlist *crtlist_new(const char *filename, int unique);
/* file loading */
-int crtlist_parse_line(char *line, char **crt_path, struct crtlist_entry *entry, const char *file, int linenum, int from_cli, char **err);
+int crtlist_parse_line(char *line, char **crt_path, struct crtlist_entry *entry, struct ckch_conf *conf, const char *file, int linenum, int from_cli, char **err);
int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *curproxy, struct crtlist **crtlist, char **err);
int crtlist_load_cert_dir(char *path, struct bind_conf *bind_conf, struct crtlist **crtlist, char **err);
diff --git a/include/haproxy/ssl_gencert.h b/include/haproxy/ssl_gencert.h
new file mode 100644
index 0000000..9065934
--- /dev/null
+++ b/include/haproxy/ssl_gencert.h
@@ -0,0 +1,35 @@
+/*
+ * include/haproxy/ssl_gencert.h
+ * This file contains definition for ssl 'generate-certificates' option.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _HAPROXY_SSL_GENCERT_H
+#define _HAPROXY_SSL_GENCERT_H
+#ifdef USE_OPENSSL
+
+#include <haproxy/listener-t.h>
+#include <haproxy/ssl_sock-t.h>
+
+int ssl_sock_generate_certificate(const char *servername, struct bind_conf *bind_conf, SSL *ssl);
+int ssl_sock_generate_certificate_from_conn(struct bind_conf *bind_conf, SSL *ssl);
+SSL_CTX *ssl_sock_assign_generated_cert(unsigned int key, struct bind_conf *bind_conf, SSL *ssl);
+SSL_CTX *ssl_sock_get_generated_cert(unsigned int key, struct bind_conf *bind_conf);
+int ssl_sock_set_generated_cert(SSL_CTX *ctx, unsigned int key, struct bind_conf *bind_conf);
+unsigned int ssl_sock_generated_cert_key(const void *data, size_t len);
+
+#endif /* USE_OPENSSL */
+#endif /* _HAPROXY_SSL_GENCERT_H */
diff --git a/include/haproxy/ssl_ocsp-t.h b/include/haproxy/ssl_ocsp-t.h
index 028d6fa..f9fef4d 100644
--- a/include/haproxy/ssl_ocsp-t.h
+++ b/include/haproxy/ssl_ocsp-t.h
@@ -47,7 +47,8 @@ struct certificate_ocsp {
struct ebmb_node key;
unsigned char key_data[OCSP_MAX_CERTID_ASN1_LENGTH];
unsigned int key_length;
- int refcount;
+ int refcount_store; /* Number of ckch_store that reference this certificate_ocsp */
+ int refcount; /* Number of actual references to this certificate_ocsp (SSL_CTXs mostly) */
struct buffer response;
long expire;
X509 *issuer;
@@ -60,8 +61,9 @@ struct certificate_ocsp {
unsigned int last_update_status;/* Status of the last OCSP update */
unsigned int num_success; /* Number of successful updates */
unsigned int num_failure; /* Number of failed updates */
- unsigned int fail_count:31; /* Number of successive failures */
+ unsigned int fail_count:30; /* Number of successive failures */
unsigned int update_once:1; /* Set if an entry should not be reinserted into te tree after update */
+ unsigned int updating:1; /* Set if an entry is already being updated */
char path[VAR_ARRAY];
};
diff --git a/include/haproxy/ssl_ocsp.h b/include/haproxy/ssl_ocsp.h
index 54a1b88..f6a72b9 100644
--- a/include/haproxy/ssl_ocsp.h
+++ b/include/haproxy/ssl_ocsp.h
@@ -36,6 +36,7 @@ int ssl_sock_get_ocsp_arg_kt_index(int evp_keytype);
int ssl_sock_ocsp_stapling_cbk(SSL *ssl, void *arg);
void ssl_sock_free_ocsp(struct certificate_ocsp *ocsp);
+void ssl_sock_free_ocsp_instance(struct certificate_ocsp *ocsp);
int ssl_sock_load_ocsp_response(struct buffer *ocsp_response,
struct certificate_ocsp *ocsp,
@@ -54,7 +55,7 @@ void ssl_destroy_ocsp_update_task(void);
int ssl_ocsp_update_insert(struct certificate_ocsp *ocsp);
-int ocsp_update_check_cfg_consistency(struct ckch_store *store, struct crtlist_entry *entry, char *crt_path, char **err);
+int ocsp_update_init(void *value, char *buf, struct ckch_data *d, int cli, char **err);
#endif /* (defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) */
diff --git a/include/haproxy/ssl_sock-t.h b/include/haproxy/ssl_sock-t.h
index fdf41a7..d111883 100644
--- a/include/haproxy/ssl_sock-t.h
+++ b/include/haproxy/ssl_sock-t.h
@@ -105,11 +105,9 @@ enum {
};
/* bind ocsp update mode */
-enum {
- SSL_SOCK_OCSP_UPDATE_DFLT = 0,
- SSL_SOCK_OCSP_UPDATE_OFF = 1,
- SSL_SOCK_OCSP_UPDATE_ON = 2,
-};
+#define SSL_SOCK_OCSP_UPDATE_OFF -1
+#define SSL_SOCK_OCSP_UPDATE_DFLT 0
+#define SSL_SOCK_OCSP_UPDATE_ON 1
/* states of the CLI IO handler for 'set ssl cert' */
enum {
@@ -264,6 +262,7 @@ struct ssl_sock_ctx {
struct global_ssl {
char *crt_base; /* base directory path for certificates */
+ char *key_base; /* base directory path for private keys */
char *ca_base; /* base directory path for CAs and CRLs */
char *issuers_chain_path; /* from "issuers-chain-path" */
int skip_self_issued_ca;
@@ -303,11 +302,14 @@ struct global_ssl {
int keylog; /* activate keylog */
int extra_files; /* which files not defined in the configuration file are we looking for */
int extra_files_noext; /* whether we remove the extension when looking up a extra file */
+ int security_level; /* configure the openssl security level */
#ifndef OPENSSL_NO_OCSP
struct {
unsigned int delay_max;
unsigned int delay_min;
+ int mode; /* default mode used for ocsp auto-update (off, on) */
+ int disable;
} ocsp_update;
#endif
};
diff --git a/include/haproxy/ssl_sock.h b/include/haproxy/ssl_sock.h
index 02d5b02..773bb32 100644
--- a/include/haproxy/ssl_sock.h
+++ b/include/haproxy/ssl_sock.h
@@ -114,18 +114,16 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *priv);
#endif
int increment_sslconn();
-SSL_CTX *ssl_sock_assign_generated_cert(unsigned int key, struct bind_conf *bind_conf, SSL *ssl);
-SSL_CTX *ssl_sock_get_generated_cert(unsigned int key, struct bind_conf *bind_conf);
-int ssl_sock_set_generated_cert(SSL_CTX *ctx, unsigned int key, struct bind_conf *bind_conf);
-unsigned int ssl_sock_generated_cert_key(const void *data, size_t len);
void ssl_sock_load_cert_sni(struct ckch_inst *ckch_inst, struct bind_conf *bind_conf);
+struct sni_ctx *ssl_sock_chose_sni_ctx(struct bind_conf *s, const char *servername,
+ int have_rsa_sig, int have_ecdsa_sig);
#ifdef SSL_MODE_ASYNC
void ssl_async_fd_handler(int fd);
void ssl_async_fd_free(int fd);
#endif
struct issuer_chain* ssl_get0_issuer_chain(X509 *cert);
int ssl_load_global_issuer_from_BIO(BIO *in, char *fp, char **err);
-int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err);
+int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, int is_default, char **err);
int ssl_sock_load_srv_cert(char *path, struct server *server, int create_if_none, char **err);
void ssl_free_global_issuers(void);
int ssl_initialize_random(void);
@@ -139,6 +137,12 @@ int ssl_get_ocspresponse_detail(unsigned char *ocsp_certid, struct buffer *out);
int ssl_ocsp_response_print(struct buffer *ocsp_response, struct buffer *out);
#endif
+#if (HA_OPENSSL_VERSION_NUMBER < 0x3000000fL)
+DH *ssl_get_tmp_dh_cbk(SSL *ssl, int export, int keylen);
+#else
+void ssl_sock_set_tmp_dh_from_pkey(SSL_CTX *ctx, EVP_PKEY *pkey);
+#endif
+
/* ssl shctx macro */
#define sh_ssl_sess_tree_delete(s) ebmb_delete(&(s)->key);
diff --git a/include/haproxy/stats-file-t.h b/include/haproxy/stats-file-t.h
new file mode 100644
index 0000000..03813e0
--- /dev/null
+++ b/include/haproxy/stats-file-t.h
@@ -0,0 +1,12 @@
+#ifndef _HAPROXY_STATS_FILE_T_H
+#define _HAPROXY_STATS_FILE_T_H
+
+/* Sections present in stats-file separated by header lines. */
+enum stfile_domain {
+ STFILE_DOMAIN_UNSET = 0,
+
+ STFILE_DOMAIN_PX_FE, /* #fe headers */
+ STFILE_DOMAIN_PX_BE, /* #be headers */
+};
+
+#endif /* _HAPROXY_STATS_FILE_T_H */
diff --git a/include/haproxy/stats-file.h b/include/haproxy/stats-file.h
new file mode 100644
index 0000000..d3853b4
--- /dev/null
+++ b/include/haproxy/stats-file.h
@@ -0,0 +1,25 @@
+#ifndef _HAPROXY_STATS_FILE_H
+#define _HAPROXY_STATS_FILE_H
+
+#include <haproxy/stats-file-t.h>
+
+#include <sys/types.h>
+
+#include <haproxy/buf-t.h>
+#include <haproxy/stats-t.h>
+
+int stats_dump_fields_file(struct buffer *out,
+ const struct field *stats, size_t stats_count,
+ struct show_stat_ctx *ctx);
+
+void stats_dump_file_header(int type, struct buffer *out);
+
+/* Maximum number of parsed stat column in a header line.
+ * Directly based on ST_I_PX_MAX, with value doubled to obtain compatibility
+ * between haproxy adjacent versions.
+ */
+#define STAT_FILE_MAX_COL_COUNT (ST_I_PX_MAX*2)
+
+void apply_stats_file(void);
+
+#endif /* _HAPROXY_STATS_FILE_H */
diff --git a/include/haproxy/stats-html-t.h b/include/haproxy/stats-html-t.h
new file mode 100644
index 0000000..1b77aea
--- /dev/null
+++ b/include/haproxy/stats-html-t.h
@@ -0,0 +1,21 @@
+#ifndef _HAPROXY_STATS_HTML_T_H
+#define _HAPROXY_STATS_HTML_T_H
+
+/* HTTP stats : applet.st0 */
+enum {
+ STAT_HTTP_INIT = 0, /* Initial state */
+ STAT_HTTP_HEAD, /* send headers before dump */
+ STAT_HTTP_DUMP, /* dumping stats */
+ STAT_HTTP_POST, /* waiting post data */
+ STAT_HTTP_LAST, /* sending last chunk of response */
+ STAT_HTTP_DONE, /* dump is finished */
+ STAT_HTTP_END, /* finished */
+};
+
+/* HTML form to limit output scope */
+#define STAT_SCOPE_TXT_MAXLEN 20 /* max len for scope substring */
+#define STAT_SCOPE_INPUT_NAME "scope" /* pattern form scope name <input> in html form */
+#define STAT_SCOPE_PATTERN "?" STAT_SCOPE_INPUT_NAME "="
+
+
+#endif /* _HAPROXY_STATS_HTML_T_H */
diff --git a/include/haproxy/stats-html.h b/include/haproxy/stats-html.h
new file mode 100644
index 0000000..912ec59
--- /dev/null
+++ b/include/haproxy/stats-html.h
@@ -0,0 +1,22 @@
+#ifndef _HAPROXY_STATS_HTML_H
+#define _HAPROXY_STATS_HTML_H
+
+#include <haproxy/stats-html-t.h>
+
+#include <haproxy/applet-t.h>
+#include <haproxy/buf-t.h>
+#include <haproxy/proxy-t.h>
+#include <haproxy/stats-t.h>
+#include <haproxy/stconn-t.h>
+
+void stats_dump_html_head(struct appctx *appctx);
+void stats_dump_html_info(struct stconn *sc);
+int stats_dump_fields_html(struct buffer *out, const struct field *stats,
+ struct show_stat_ctx *ctx);
+void stats_dump_html_px_hdr(struct stconn *sc, struct proxy *px);
+void stats_dump_html_px_end(struct stconn *sc, struct proxy *px);
+void stats_dump_html_end(struct buffer *out);
+
+extern struct applet http_stats_applet;
+
+#endif /* _HAPROXY_STATS_HTML_H */
diff --git a/include/haproxy/stats-json.h b/include/haproxy/stats-json.h
new file mode 100644
index 0000000..d6c4382
--- /dev/null
+++ b/include/haproxy/stats-json.h
@@ -0,0 +1,24 @@
+#ifndef _HAPROXY_STATS_JSON_H
+#define _HAPROXY_STATS_JSON_H
+
+#include <haproxy/applet-t.h>
+#include <haproxy/buf-t.h>
+#include <haproxy/stats-t.h>
+
+void stats_dump_json_header(struct buffer *out);
+
+int stats_dump_fields_json(struct buffer *out,
+ const struct field *stats, size_t stats_count,
+ struct show_stat_ctx *ctx);
+
+void stats_dump_json_end(struct buffer *out);
+
+int stats_dump_json_info_fields(struct buffer *out,
+ const struct field *info,
+ struct show_stat_ctx *ctx);
+
+void stats_dump_json_schema(struct buffer *out);
+
+int stats_dump_json_schema_to_buffer(struct appctx *appctx);
+
+#endif /* _HAPROXY_STATS_JSON_H */
diff --git a/include/haproxy/stats-proxy.h b/include/haproxy/stats-proxy.h
new file mode 100644
index 0000000..81a60f0
--- /dev/null
+++ b/include/haproxy/stats-proxy.h
@@ -0,0 +1,14 @@
+#ifndef _HAPROXY_STATS_PROXY_H
+#define _HAPROXY_STATS_PROXY_H
+
+#include <haproxy/api-t.h>
+
+struct buffer;
+struct htx;
+struct stconn;
+
+int stats_dump_proxies(struct stconn *sc, struct buffer *buf, struct htx *htx);
+
+void proxy_stats_clear_counters(int clrall, struct list *stat_modules);
+
+#endif /* _HAPROXY_STATS_PROXY_H */
diff --git a/include/haproxy/stats-t.h b/include/haproxy/stats-t.h
index 34a4cc2..d4d01e9 100644
--- a/include/haproxy/stats-t.h
+++ b/include/haproxy/stats-t.h
@@ -22,32 +22,35 @@
#ifndef _HAPROXY_STATS_T_H
#define _HAPROXY_STATS_T_H
+#include <import/ebtree-t.h>
#include <haproxy/api-t.h>
+#include <haproxy/buf-t.h>
/* Flags for applet.ctx.stats.flags */
-#define STAT_FMT_HTML 0x00000001 /* dump the stats in HTML format */
-#define STAT_FMT_TYPED 0x00000002 /* use the typed output format */
-#define STAT_FMT_JSON 0x00000004 /* dump the stats in JSON format */
-#define STAT_HIDE_DOWN 0x00000008 /* hide 'down' servers in the stats page */
-#define STAT_NO_REFRESH 0x00000010 /* do not automatically refresh the stats page */
-#define STAT_ADMIN 0x00000020 /* indicate a stats admin level */
-#define STAT_CHUNKED 0x00000040 /* use chunked encoding (HTTP/1.1) */
-#define STAT_JSON_SCHM 0x00000080 /* dump the json schema */
-
-#define STAT_HIDEVER 0x00000100 /* conf: do not report the version and reldate */
-#define STAT_SHNODE 0x00000200 /* conf: show node name */
-#define STAT_SHDESC 0x00000400 /* conf: show description */
-#define STAT_SHLGNDS 0x00000800 /* conf: show legends */
-#define STAT_SHOW_FDESC 0x00001000 /* show the field descriptions when possible */
-#define STAT_SHMODULES 0x00002000 /* conf: show modules */
-#define STAT_HIDE_MAINT 0x00004000 /* hide maint/disabled servers */
-#define STAT_CONVDONE 0x00008000 /* conf: rules conversion done */
-#define STAT_USE_FLOAT 0x00010000 /* use floats where possible in the outputs */
-
-#define STAT_BOUND 0x00800000 /* bound statistics to selected proxies/types/services */
-#define STAT_STARTED 0x01000000 /* some output has occurred */
-
-#define STAT_FMT_MASK 0x00000007
+#define STAT_F_FMT_HTML 0x00000001 /* dump the stats in HTML format */
+#define STAT_F_FMT_TYPED 0x00000002 /* use the typed output format */
+#define STAT_F_FMT_JSON 0x00000004 /* dump the stats in JSON format */
+#define STAT_F_FMT_FILE 0x00000008 /* dump stats-file */
+#define STAT_F_NO_REFRESH 0x00000010 /* do not automatically refresh the stats page */
+#define STAT_F_ADMIN 0x00000020 /* indicate a stats admin level */
+#define STAT_F_CHUNKED 0x00000040 /* use chunked encoding (HTTP/1.1) */
+#define STAT_F_JSON_SCHM 0x00000080 /* dump the json schema */
+
+#define STAT_F_HIDEVER 0x00000100 /* conf: do not report the version and reldate */
+#define STAT_F_SHNODE 0x00000200 /* conf: show node name */
+#define STAT_F_SHDESC 0x00000400 /* conf: show description */
+#define STAT_F_SHLGNDS 0x00000800 /* conf: show legends */
+#define STAT_F_SHOW_FDESC 0x00001000 /* show the column descriptions when possible */
+#define STAT_F_SHMODULES 0x00002000 /* conf: show modules */
+#define STAT_F_HIDE_MAINT 0x00004000 /* hide maint/disabled servers */
+#define STAT_F_CONVDONE 0x00008000 /* conf: rules conversion done */
+#define STAT_F_USE_FLOAT 0x00010000 /* use floats where possible in the outputs */
+#define STAT_F_HIDE_DOWN 0x00020000 /* hide 'down' servers in the stats page */
+
+#define STAT_F_BOUND 0x00800000 /* bound statistics to selected proxies/types/services */
+#define STAT_F_STARTED 0x01000000 /* some output has occurred */
+
+#define STAT_F_FMT_MASK 0x0000000f
#define STATS_TYPE_FE 0
#define STATS_TYPE_BE 1
@@ -57,17 +60,6 @@
#define STATS_DOMAIN (0) /* used for bitshifting, type of statistics: proxy or dns */
#define STATS_PX_CAP (8) /* used for bitshifting, differentiate obj1 type for proxy statistics */
-/* HTTP stats : applet.st0 */
-enum {
- STAT_HTTP_INIT = 0, /* Initial state */
- STAT_HTTP_HEAD, /* send headers before dump */
- STAT_HTTP_DUMP, /* dumping stats */
- STAT_HTTP_POST, /* waiting post data */
- STAT_HTTP_LAST, /* sending last chunk of response */
- STAT_HTTP_DONE, /* dump is finished */
- STAT_HTTP_END, /* finished */
-};
-
/* status codes available for the stats admin page */
enum {
STAT_STATUS_INIT = 0,
@@ -82,11 +74,6 @@ enum {
STAT_STATUS_SIZE
};
-/* HTML form to limit output scope */
-#define STAT_SCOPE_TXT_MAXLEN 20 /* max len for scope substring */
-#define STAT_SCOPE_INPUT_NAME "scope" /* pattern form scope name <input> in html form */
-#define STAT_SCOPE_PATTERN "?" STAT_SCOPE_INPUT_NAME "="
-
/* Actions available for the stats admin forms */
enum {
ST_ADM_ACTION_NONE = 0,
@@ -265,224 +252,240 @@ enum field_scope {
FS_MASK = 0xFF000000,
};
-/* Show info fields for CLI output. For any field added here, please add the
- * text representation in the info_fields array. Please only append at the end,
- * before the INF_TOTAL_FIELDS entry, and never insert anything in the middle
+/* Show info columns for CLI output. For any column added here, please add the
+ * text representation in the metrics_info array. Please only append at the end,
+ * before the ST_I_INF_MAX entry, and never insert anything in the middle
* nor at the beginning.
*/
-enum info_field {
- INF_NAME,
- INF_VERSION,
- INF_RELEASE_DATE,
- INF_NBTHREAD,
- INF_NBPROC,
- INF_PROCESS_NUM,
- INF_PID,
- INF_UPTIME,
- INF_UPTIME_SEC,
- INF_MEMMAX_MB,
- INF_POOL_ALLOC_MB,
- INF_POOL_USED_MB,
- INF_POOL_FAILED,
- INF_ULIMIT_N,
- INF_MAXSOCK,
- INF_MAXCONN,
- INF_HARD_MAXCONN,
- INF_CURR_CONN,
- INF_CUM_CONN,
- INF_CUM_REQ,
- INF_MAX_SSL_CONNS,
- INF_CURR_SSL_CONNS,
- INF_CUM_SSL_CONNS,
- INF_MAXPIPES,
- INF_PIPES_USED,
- INF_PIPES_FREE,
- INF_CONN_RATE,
- INF_CONN_RATE_LIMIT,
- INF_MAX_CONN_RATE,
- INF_SESS_RATE,
- INF_SESS_RATE_LIMIT,
- INF_MAX_SESS_RATE,
- INF_SSL_RATE,
- INF_SSL_RATE_LIMIT,
- INF_MAX_SSL_RATE,
- INF_SSL_FRONTEND_KEY_RATE,
- INF_SSL_FRONTEND_MAX_KEY_RATE,
- INF_SSL_FRONTEND_SESSION_REUSE_PCT,
- INF_SSL_BACKEND_KEY_RATE,
- INF_SSL_BACKEND_MAX_KEY_RATE,
- INF_SSL_CACHE_LOOKUPS,
- INF_SSL_CACHE_MISSES,
- INF_COMPRESS_BPS_IN,
- INF_COMPRESS_BPS_OUT,
- INF_COMPRESS_BPS_RATE_LIM,
- INF_ZLIB_MEM_USAGE,
- INF_MAX_ZLIB_MEM_USAGE,
- INF_TASKS,
- INF_RUN_QUEUE,
- INF_IDLE_PCT,
- INF_NODE,
- INF_DESCRIPTION,
- INF_STOPPING,
- INF_JOBS,
- INF_UNSTOPPABLE_JOBS,
- INF_LISTENERS,
- INF_ACTIVE_PEERS,
- INF_CONNECTED_PEERS,
- INF_DROPPED_LOGS,
- INF_BUSY_POLLING,
- INF_FAILED_RESOLUTIONS,
- INF_TOTAL_BYTES_OUT,
- INF_TOTAL_SPLICED_BYTES_OUT,
- INF_BYTES_OUT_RATE,
- INF_DEBUG_COMMANDS_ISSUED,
- INF_CUM_LOG_MSGS,
- INF_BUILD_INFO,
- INF_MEMMAX_BYTES,
- INF_POOL_ALLOC_BYTES,
- INF_POOL_USED_BYTES,
- INF_START_TIME_SEC,
- INF_TAINTED,
- INF_WARNINGS,
- INF_MAXCONN_REACHED,
- INF_BOOTTIME_MS,
- INF_NICED_TASKS,
+enum stat_idx_info {
+ ST_I_INF_NAME,
+ ST_I_INF_VERSION,
+ ST_I_INF_RELEASE_DATE,
+ ST_I_INF_NBTHREAD,
+ ST_I_INF_NBPROC,
+ ST_I_INF_PROCESS_NUM,
+ ST_I_INF_PID,
+ ST_I_INF_UPTIME,
+ ST_I_INF_UPTIME_SEC,
+ ST_I_INF_MEMMAX_MB,
+ ST_I_INF_POOL_ALLOC_MB,
+ ST_I_INF_POOL_USED_MB,
+ ST_I_INF_POOL_FAILED,
+ ST_I_INF_ULIMIT_N,
+ ST_I_INF_MAXSOCK,
+ ST_I_INF_MAXCONN,
+ ST_I_INF_HARD_MAXCONN,
+ ST_I_INF_CURR_CONN,
+ ST_I_INF_CUM_CONN,
+ ST_I_INF_CUM_REQ,
+ ST_I_INF_MAX_SSL_CONNS,
+ ST_I_INF_CURR_SSL_CONNS,
+ ST_I_INF_CUM_SSL_CONNS,
+ ST_I_INF_MAXPIPES,
+ ST_I_INF_PIPES_USED,
+ ST_I_INF_PIPES_FREE,
+ ST_I_INF_CONN_RATE,
+ ST_I_INF_CONN_RATE_LIMIT,
+ ST_I_INF_MAX_CONN_RATE,
+ ST_I_INF_SESS_RATE,
+ ST_I_INF_SESS_RATE_LIMIT,
+ ST_I_INF_MAX_SESS_RATE,
+ ST_I_INF_SSL_RATE,
+ ST_I_INF_SSL_RATE_LIMIT,
+ ST_I_INF_MAX_SSL_RATE,
+ ST_I_INF_SSL_FRONTEND_KEY_RATE,
+ ST_I_INF_SSL_FRONTEND_MAX_KEY_RATE,
+ ST_I_INF_SSL_FRONTEND_SESSION_REUSE_PCT,
+ ST_I_INF_SSL_BACKEND_KEY_RATE,
+ ST_I_INF_SSL_BACKEND_MAX_KEY_RATE,
+ ST_I_INF_SSL_CACHE_LOOKUPS,
+ ST_I_INF_SSL_CACHE_MISSES,
+ ST_I_INF_COMPRESS_BPS_IN,
+ ST_I_INF_COMPRESS_BPS_OUT,
+ ST_I_INF_COMPRESS_BPS_RATE_LIM,
+ ST_I_INF_ZLIB_MEM_USAGE,
+ ST_I_INF_MAX_ZLIB_MEM_USAGE,
+ ST_I_INF_TASKS,
+ ST_I_INF_RUN_QUEUE,
+ ST_I_INF_IDLE_PCT,
+ ST_I_INF_NODE,
+ ST_I_INF_DESCRIPTION,
+ ST_I_INF_STOPPING,
+ ST_I_INF_JOBS,
+ ST_I_INF_UNSTOPPABLE_JOBS,
+ ST_I_INF_LISTENERS,
+ ST_I_INF_ACTIVE_PEERS,
+ ST_I_INF_CONNECTED_PEERS,
+ ST_I_INF_DROPPED_LOGS,
+ ST_I_INF_BUSY_POLLING,
+ ST_I_INF_FAILED_RESOLUTIONS,
+ ST_I_INF_TOTAL_BYTES_OUT,
+ ST_I_INF_TOTAL_SPLICED_BYTES_OUT,
+ ST_I_INF_BYTES_OUT_RATE,
+ ST_I_INF_DEBUG_COMMANDS_ISSUED,
+ ST_I_INF_CUM_LOG_MSGS,
+ ST_I_INF_BUILD_INFO,
+ ST_I_INF_MEMMAX_BYTES,
+ ST_I_INF_POOL_ALLOC_BYTES,
+ ST_I_INF_POOL_USED_BYTES,
+ ST_I_INF_START_TIME_SEC,
+ ST_I_INF_TAINTED,
+ ST_I_INF_WARNINGS,
+ ST_I_INF_MAXCONN_REACHED,
+ ST_I_INF_BOOTTIME_MS,
+ ST_I_INF_NICED_TASKS,
/* must always be the last one */
- INF_TOTAL_FIELDS
+ ST_I_INF_MAX
+};
+
+/* Represent an exposed statistic. */
+struct stat_col {
+ const char *name; /* short name, used notably in CSV headers */
+ const char *desc; /* user-friendly description */
+
+ uint32_t type; /* combination of field_nature and field_format */
+ uint8_t cap; /* mask of stats_domain_px_cap to restrain metrics to an object types subset */
+
+ /* used only for generic metrics */
+ struct {
+ int offset[2]; /* offset in counters */
+ } metric;
};
-/* Stats fields for CSV output. For any field added here, please add the text
- * representation in the stat_fields array. Please only append at the end,
- * before the ST_F_TOTAL_FIELDS entry, and never insert anything in the middle
+/* Stats columns for CSV output. For any column added here, please add the text
+ * representation in the metrics_px array. Please only append at the end,
+ * before the ST_I_PX_MAX entry, and never insert anything in the middle
* nor at the beginning.When adding an entry here, one must always add a
- * corresponding one in stat_fields[] otherwise Lua's get_stats() will break,
+ * corresponding one in metrics_px[] otherwise Lua's get_stats() will break,
* and "show stats" will show a null.
*/
-enum stat_field {
- ST_F_PXNAME,
- ST_F_SVNAME,
- ST_F_QCUR,
- ST_F_QMAX,
- ST_F_SCUR,
- ST_F_SMAX,
- ST_F_SLIM,
- ST_F_STOT,
- ST_F_BIN ,
- ST_F_BOUT,
- ST_F_DREQ,
- ST_F_DRESP,
- ST_F_EREQ,
- ST_F_ECON,
- ST_F_ERESP,
- ST_F_WRETR,
- ST_F_WREDIS,
- ST_F_STATUS,
- ST_F_WEIGHT,
- ST_F_ACT,
- ST_F_BCK,
- ST_F_CHKFAIL,
- ST_F_CHKDOWN,
- ST_F_LASTCHG,
- ST_F_DOWNTIME,
- ST_F_QLIMIT,
- ST_F_PID,
- ST_F_IID,
- ST_F_SID,
- ST_F_THROTTLE,
- ST_F_LBTOT,
- ST_F_TRACKED,
- ST_F_TYPE,
- ST_F_RATE,
- ST_F_RATE_LIM,
- ST_F_RATE_MAX,
- ST_F_CHECK_STATUS,
- ST_F_CHECK_CODE,
- ST_F_CHECK_DURATION,
- ST_F_HRSP_1XX,
- ST_F_HRSP_2XX,
- ST_F_HRSP_3XX,
- ST_F_HRSP_4XX,
- ST_F_HRSP_5XX,
- ST_F_HRSP_OTHER,
- ST_F_HANAFAIL,
- ST_F_REQ_RATE,
- ST_F_REQ_RATE_MAX,
- ST_F_REQ_TOT,
- ST_F_CLI_ABRT,
- ST_F_SRV_ABRT,
- ST_F_COMP_IN,
- ST_F_COMP_OUT,
- ST_F_COMP_BYP,
- ST_F_COMP_RSP,
- ST_F_LASTSESS,
- ST_F_LAST_CHK,
- ST_F_LAST_AGT,
- ST_F_QTIME,
- ST_F_CTIME,
- ST_F_RTIME,
- ST_F_TTIME,
- ST_F_AGENT_STATUS,
- ST_F_AGENT_CODE,
- ST_F_AGENT_DURATION,
- ST_F_CHECK_DESC,
- ST_F_AGENT_DESC,
- ST_F_CHECK_RISE,
- ST_F_CHECK_FALL,
- ST_F_CHECK_HEALTH,
- ST_F_AGENT_RISE,
- ST_F_AGENT_FALL,
- ST_F_AGENT_HEALTH,
- ST_F_ADDR,
- ST_F_COOKIE,
- ST_F_MODE,
- ST_F_ALGO,
- ST_F_CONN_RATE,
- ST_F_CONN_RATE_MAX,
- ST_F_CONN_TOT,
- ST_F_INTERCEPTED,
- ST_F_DCON,
- ST_F_DSES,
- ST_F_WREW,
- ST_F_CONNECT,
- ST_F_REUSE,
- ST_F_CACHE_LOOKUPS,
- ST_F_CACHE_HITS,
- ST_F_SRV_ICUR,
- ST_F_SRV_ILIM,
- ST_F_QT_MAX,
- ST_F_CT_MAX,
- ST_F_RT_MAX,
- ST_F_TT_MAX,
- ST_F_EINT,
- ST_F_IDLE_CONN_CUR,
- ST_F_SAFE_CONN_CUR,
- ST_F_USED_CONN_CUR,
- ST_F_NEED_CONN_EST,
- ST_F_UWEIGHT,
- ST_F_AGG_SRV_STATUS,
- ST_F_AGG_SRV_CHECK_STATUS,
- ST_F_AGG_CHECK_STATUS,
- ST_F_SRID,
- ST_F_SESS_OTHER,
- ST_F_H1SESS,
- ST_F_H2SESS,
- ST_F_H3SESS,
- ST_F_REQ_OTHER,
- ST_F_H1REQ,
- ST_F_H2REQ,
- ST_F_H3REQ,
- ST_F_PROTO,
+enum stat_idx_px {
+ ST_I_PX_PXNAME,
+ ST_I_PX_SVNAME,
+ ST_I_PX_QCUR,
+ ST_I_PX_QMAX,
+ ST_I_PX_SCUR,
+ ST_I_PX_SMAX,
+ ST_I_PX_SLIM,
+ ST_I_PX_STOT,
+ ST_I_PX_BIN ,
+ ST_I_PX_BOUT,
+ ST_I_PX_DREQ,
+ ST_I_PX_DRESP,
+ ST_I_PX_EREQ,
+ ST_I_PX_ECON,
+ ST_I_PX_ERESP,
+ ST_I_PX_WRETR,
+ ST_I_PX_WREDIS,
+ ST_I_PX_STATUS,
+ ST_I_PX_WEIGHT,
+ ST_I_PX_ACT,
+ ST_I_PX_BCK,
+ ST_I_PX_CHKFAIL,
+ ST_I_PX_CHKDOWN,
+ ST_I_PX_LASTCHG,
+ ST_I_PX_DOWNTIME,
+ ST_I_PX_QLIMIT,
+ ST_I_PX_PID,
+ ST_I_PX_IID,
+ ST_I_PX_SID,
+ ST_I_PX_THROTTLE,
+ ST_I_PX_LBTOT,
+ ST_I_PX_TRACKED,
+ ST_I_PX_TYPE,
+ ST_I_PX_RATE,
+ ST_I_PX_RATE_LIM,
+ ST_I_PX_RATE_MAX,
+ ST_I_PX_CHECK_STATUS,
+ ST_I_PX_CHECK_CODE,
+ ST_I_PX_CHECK_DURATION,
+ ST_I_PX_HRSP_1XX,
+ ST_I_PX_HRSP_2XX,
+ ST_I_PX_HRSP_3XX,
+ ST_I_PX_HRSP_4XX,
+ ST_I_PX_HRSP_5XX,
+ ST_I_PX_HRSP_OTHER,
+ ST_I_PX_HANAFAIL,
+ ST_I_PX_REQ_RATE,
+ ST_I_PX_REQ_RATE_MAX,
+ ST_I_PX_REQ_TOT,
+ ST_I_PX_CLI_ABRT,
+ ST_I_PX_SRV_ABRT,
+ ST_I_PX_COMP_IN,
+ ST_I_PX_COMP_OUT,
+ ST_I_PX_COMP_BYP,
+ ST_I_PX_COMP_RSP,
+ ST_I_PX_LASTSESS,
+ ST_I_PX_LAST_CHK,
+ ST_I_PX_LAST_AGT,
+ ST_I_PX_QTIME,
+ ST_I_PX_CTIME,
+ ST_I_PX_RTIME,
+ ST_I_PX_TTIME,
+ ST_I_PX_AGENT_STATUS,
+ ST_I_PX_AGENT_CODE,
+ ST_I_PX_AGENT_DURATION,
+ ST_I_PX_CHECK_DESC,
+ ST_I_PX_AGENT_DESC,
+ ST_I_PX_CHECK_RISE,
+ ST_I_PX_CHECK_FALL,
+ ST_I_PX_CHECK_HEALTH,
+ ST_I_PX_AGENT_RISE,
+ ST_I_PX_AGENT_FALL,
+ ST_I_PX_AGENT_HEALTH,
+ ST_I_PX_ADDR,
+ ST_I_PX_COOKIE,
+ ST_I_PX_MODE,
+ ST_I_PX_ALGO,
+ ST_I_PX_CONN_RATE,
+ ST_I_PX_CONN_RATE_MAX,
+ ST_I_PX_CONN_TOT,
+ ST_I_PX_INTERCEPTED,
+ ST_I_PX_DCON,
+ ST_I_PX_DSES,
+ ST_I_PX_WREW,
+ ST_I_PX_CONNECT,
+ ST_I_PX_REUSE,
+ ST_I_PX_CACHE_LOOKUPS,
+ ST_I_PX_CACHE_HITS,
+ ST_I_PX_SRV_ICUR,
+ ST_I_PX_SRV_ILIM,
+ ST_I_PX_QT_MAX,
+ ST_I_PX_CT_MAX,
+ ST_I_PX_RT_MAX,
+ ST_I_PX_TT_MAX,
+ ST_I_PX_EINT,
+ ST_I_PX_IDLE_CONN_CUR,
+ ST_I_PX_SAFE_CONN_CUR,
+ ST_I_PX_USED_CONN_CUR,
+ ST_I_PX_NEED_CONN_EST,
+ ST_I_PX_UWEIGHT,
+ ST_I_PX_AGG_SRV_STATUS,
+ ST_I_PX_AGG_SRV_CHECK_STATUS,
+ ST_I_PX_AGG_CHECK_STATUS,
+ ST_I_PX_SRID,
+ ST_I_PX_SESS_OTHER,
+ ST_I_PX_H1SESS,
+ ST_I_PX_H2SESS,
+ ST_I_PX_H3SESS,
+ ST_I_PX_REQ_OTHER,
+ ST_I_PX_H1REQ,
+ ST_I_PX_H2REQ,
+ ST_I_PX_H3REQ,
+ ST_I_PX_PROTO,
/* must always be the last one */
- ST_F_TOTAL_FIELDS
+ ST_I_PX_MAX
+};
+
+/* Node for name-indexed stat tree from generate_stat_tree(). */
+struct stcol_node {
+ const struct stat_col *col;
+ struct ebmb_node name;
};
-/* Please consider updating stats_dump_fields_*(),
- * stats_dump_.*_info_fields() and stats_*_schema()
- * when modifying struct field or related enums.
- */
struct field {
uint32_t type;
union {
@@ -511,9 +514,9 @@ struct stats_module {
const char *name;
/* functor used to generate the stats module using counters provided through data parameter */
- void (*fill_stats)(void *data, struct field *);
+ int (*fill_stats)(void *data, struct field *, unsigned int *);
- struct name_desc *stats; /* name/description of stats provided by the module */
+ struct stat_col *stats; /* statistics provided by the module */
void *counters; /* initial values of allocated counters */
size_t counters_off[COUNTERS_OFF_END]; /* list of offsets of allocated counters in various objects */
size_t stats_count; /* count of stats provided */
@@ -548,6 +551,16 @@ enum stats_domain_px_cap {
STATS_PX_CAP_MASK = 0xff
};
+/* Shortcut names for enum stats_domain_px_cap only for declaration convenience */
+#define STATS_PX_CAP_LFBS (STATS_PX_CAP_MASK)
+#define STATS_PX_CAP_LFB_ (STATS_PX_CAP_FE|STATS_PX_CAP_BE|STATS_PX_CAP_LI)
+#define STATS_PX_CAP_LF__ (STATS_PX_CAP_FE|STATS_PX_CAP_LI)
+#define STATS_PX_CAP__FBS (STATS_PX_CAP_FE|STATS_PX_CAP_BE|STATS_PX_CAP_SRV)
+#define STATS_PX_CAP__FB_ (STATS_PX_CAP_FE|STATS_PX_CAP_BE)
+#define STATS_PX_CAP__F__ (STATS_PX_CAP_FE)
+#define STATS_PX_CAP___BS (STATS_PX_CAP_BE|STATS_PX_CAP_SRV)
+#define STATS_PX_CAP____S (STATS_PX_CAP_SRV)
+
/* the context of a "show stat" command in progress on the CLI or the stats applet */
struct show_stat_ctx {
struct proxy *http_px; /* parent proxy of the current applet (only relevant for HTTP applet) */
@@ -558,9 +571,10 @@ struct show_stat_ctx {
int scope_len; /* length of the string above in the buffer */
int field; /* current field iterator when stat line is dumped through returning function */
int px_st; /* STAT_PX_ST* */
- unsigned int flags; /* STAT_* from stats-t.h */
+ unsigned int flags; /* STAT_F_* from stats-t.h */
int iid, type, sid; /* proxy id, type and service id if bounding of stats is enabled */
int st_code; /* the status code returned by an action */
+ struct buffer chunk; /* temporary buffer which holds a single-line output */
enum stat_state state; /* phase of output production */
};
diff --git a/include/haproxy/stats.h b/include/haproxy/stats.h
index f9e6d97..e227f3f 100644
--- a/include/haproxy/stats.h
+++ b/include/haproxy/stats.h
@@ -33,31 +33,42 @@ struct buffer;
struct proxy;
struct appctx;
struct htx;
+struct stconn;
-/* These two structs contains all field names and descriptions according to
- * the the number of entries in "enum stat_field" and "enum info_field"
+/* These two structs contains all column names and descriptions according to
+ * the the number of entries in "enum stat_idx_px" and "enum stat_idx_info"
*/
-extern const struct name_desc stat_fields[];
-extern const struct name_desc info_fields[];
+extern const struct stat_col stat_cols_px[];
+extern const struct name_desc stat_cols_info[];
extern const char *stat_status_codes[];
extern struct applet http_stats_applet;
-extern THREAD_LOCAL struct field info[];
-extern THREAD_LOCAL struct field *stat_l[];
+extern struct list stats_module_list[];
+extern THREAD_LOCAL struct field stat_line_info[];
+extern THREAD_LOCAL struct field *stat_lines[];
+extern struct name_desc *stat_cols[STATS_DOMAIN_COUNT];
+extern size_t stat_cols_len[STATS_DOMAIN_COUNT];
+
+int generate_stat_tree(struct eb_root *st_tree, const struct stat_col cols[]);
struct htx;
-int stats_putchk(struct appctx *appctx, struct htx *htx);
+int stats_putchk(struct appctx *appctx, struct buffer *buf, struct htx *htx);
+int stats_is_full(struct appctx *appctx, struct buffer *buf, struct htx *htx);
+
+const char *stats_scope_ptr(struct appctx *appctx);
-int stats_dump_one_line(const struct field *stats, size_t stats_count, struct appctx *appctx);
+int stats_dump_one_line(const struct field *line, size_t stats_count, struct appctx *appctx);
int stats_fill_info(struct field *info, int len, uint flags);
-int stats_fill_fe_stats(struct proxy *px, struct field *stats, int len,
- enum stat_field *selected_field);
-int stats_fill_li_stats(struct proxy *px, struct listener *l, int flags,
- struct field *stats, int len, enum stat_field *selected_field);
-int stats_fill_sv_stats(struct proxy *px, struct server *sv, int flags,
- struct field *stats, int len, enum stat_field *selected_field);
-int stats_fill_be_stats(struct proxy *px, int flags, struct field *stats, int len,
- enum stat_field *selected_field);
+int stats_fill_fe_line(struct proxy *px, int flags, struct field *line, int len,
+ enum stat_idx_px *index);
+int stats_fill_li_line(struct proxy *px, struct listener *l, int flags,
+ struct field *line, int len, enum stat_idx_px *index);
+int stats_fill_sv_line(struct proxy *px, struct server *sv, int flags,
+ struct field *line, int len, enum stat_idx_px *index);
+int stats_fill_be_line(struct proxy *px, int flags, struct field *line, int len,
+ enum stat_idx_px *index);
+
+int stats_dump_stat_to_buffer(struct stconn *sc, struct buffer *buf, struct htx *htx);
int stats_emit_raw_data_field(struct buffer *out, const struct field *f);
int stats_emit_typed_data_field(struct buffer *out, const struct field *f);
@@ -65,6 +76,22 @@ int stats_emit_field_tags(struct buffer *out, const struct field *f,
char delim);
+/* Returns true if <col> is fully defined, false if only used as name-desc. */
+static inline int stcol_is_generic(const struct stat_col *col)
+{
+ return !!(col->cap);
+}
+
+static inline enum field_format stcol_format(const struct stat_col *col)
+{
+ return col->type & FF_MASK;
+}
+
+static inline enum field_nature stcol_nature(const struct stat_col *col)
+{
+ return col->type & FN_MASK;
+}
+
static inline enum field_format field_format(const struct field *f, int e)
{
return f[e].type & FF_MASK;
@@ -129,6 +156,16 @@ static inline struct field mkf_flt(uint32_t type, double value)
#define MK_STATS_PROXY_DOMAIN(px_cap) \
((px_cap) << STATS_PX_CAP | STATS_DOMAIN_PROXY)
+static inline uint8_t stats_get_domain(uint32_t domain)
+{
+ return domain >> STATS_DOMAIN & STATS_DOMAIN_MASK;
+}
+
+static inline enum stats_domain_px_cap stats_px_get_cap(uint32_t domain)
+{
+ return domain >> STATS_PX_CAP & STATS_PX_CAP_MASK;
+}
+
int stats_allocate_proxy_counters_internal(struct extra_counters **counters,
int type, int px_cap);
int stats_allocate_proxy_counters(struct proxy *px);
diff --git a/include/haproxy/stconn-t.h b/include/haproxy/stconn-t.h
index 63bcb79..f418e95 100644
--- a/include/haproxy/stconn-t.h
+++ b/include/haproxy/stconn-t.h
@@ -26,6 +26,7 @@
#include <haproxy/connection-t.h>
#include <haproxy/pipe-t.h>
#include <haproxy/show_flags-t.h>
+#include <haproxy/task-t.h>
#include <haproxy/xref-t.h>
enum iobuf_flags {
@@ -40,6 +41,13 @@ enum iobuf_flags {
IOBUF_FL_EOI = 0x00000010, /* A EOI was encountered on producer side */
};
+/* Flags used */
+enum nego_ff_flags {
+ NEGO_FF_FL_NONE = 0x00000000, /* For initialization purposes */
+ NEGO_FF_FL_MAY_SPLICE = 0x00000001, /* Consumer may choose to use kernel splicing if it supports it */
+ NEGO_FF_FL_EXACT_SIZE = 0x00000002, /* Size passed for the nego is the expected exact size to forwarded */
+};
+
struct iobuf {
struct pipe *pipe; /* non-NULL only when data present */
struct buffer *buf;
@@ -107,6 +115,14 @@ enum se_flags {
SE_FL_APPLET_NEED_CONN = 0x80000000, /* applet is waiting for the other side to (fail to) connect */
};
+/* Shutdown modes */
+enum se_shut_mode {
+ SE_SHR_DRAIN = 0x00000001, /* read shutdown, drain any extra stuff */
+ SE_SHR_RESET = 0x00000002, /* read shutdown, reset any extra stuff */
+ SE_SHW_NORMAL = 0x00000004, /* regular write shutdown */
+ SE_SHW_SILENT = 0x00000008, /* imminent close, don't notify peer */
+};
+
/* This function is used to report flags in debugging tools. Please reflect
* below any single-bit flag addition above in the same order via the
* __APPEND_FLAG macro. The new end of the buffer is returned.
@@ -188,6 +204,7 @@ enum sc_flags {
SC_FL_SHUT_DONE = 0x00020000, /* A shutdown was performed for the SC */
SC_FL_EOS = 0x00040000, /* End of stream was reached (from down side to up side) */
+ SC_FL_HAVE_BUFF = 0x00080000, /* A buffer is ready, flag will be cleared once allocated */
};
/* This function is used to report flags in debugging tools. Please reflect
@@ -205,7 +222,7 @@ static forceinline char *sc_show_flags(char *buf, size_t len, const char *delim,
_(SC_FL_NEED_BUFF, _(SC_FL_NEED_ROOM,
_(SC_FL_RCV_ONCE, _(SC_FL_SND_ASAP, _(SC_FL_SND_NEVERWAIT, _(SC_FL_SND_EXP_MORE,
_(SC_FL_ABRT_WANTED, _(SC_FL_SHUT_WANTED, _(SC_FL_ABRT_DONE, _(SC_FL_SHUT_DONE,
- _(SC_FL_EOS)))))))))))))))))));
+ _(SC_FL_EOS, _(SC_FL_HAVE_BUFF))))))))))))))))))));
/* epilogue */
_(~0U);
return buf;
@@ -250,6 +267,24 @@ enum sc_state_bit {
struct stconn;
+/* represent the abort code, enriched with contextual info:
+ * - First 5 bits are used for the source (31 possible sources)
+ * - other bits are reserved for now
+ */
+#define SE_ABRT_SRC_SHIFT 0
+#define SE_ABRT_SRC_MASK 0x0000001f
+
+#define SE_ABRT_SRC_MUX_PT 0x01 /* Code set by the PT mux */
+#define SE_ABRT_SRC_MUX_H1 0x02 /* Code set bu the H1 mux */
+#define SE_ABRT_SRC_MUX_H2 0x03 /* Code set bu the H2 mux */
+#define SE_ABRT_SRC_MUX_QUIC 0x04 /* Code set bu the QUIC/H3 mux */
+#define SE_ABRT_SRC_MUX_FCGI 0x05 /* Code set bu the FCGI mux */
+
+struct se_abort_info {
+ uint32_t info;
+ uint64_t code;
+};
+
/* A Stream Endpoint Descriptor (sedesc) is the link between the stream
* connector (ex. stconn) and the Stream Endpoint (mux or appctx).
* It always exists for either of them, and binds them together. It also
@@ -280,6 +315,7 @@ struct sedesc {
struct stconn *sc; /* the stream connector we're attached to, or NULL */
struct iobuf iobuf; /* contains data forwarded by the other side and that must be sent by the stream endpoint */
unsigned int flags; /* SE_FL_* */
+ struct se_abort_info abort_info; /* Info about abort, as reported by the endpoint and eventually enriched by the app level */
unsigned int lra; /* the last read activity */
unsigned int fsb; /* the first send blocked */
/* 4 bytes hole here */
diff --git a/include/haproxy/stconn.h b/include/haproxy/stconn.h
index 7869fa3..f60eaa8 100644
--- a/include/haproxy/stconn.h
+++ b/include/haproxy/stconn.h
@@ -34,11 +34,13 @@ struct appctx;
struct stream;
struct check;
-#define IS_HTX_SC(sc) (sc_conn(sc) && IS_HTX_CONN(__sc_conn(sc)))
+#define IS_HTX_SC(sc) ((sc_conn(sc) && IS_HTX_CONN(__sc_conn(sc))) || (sc_appctx(sc) && IS_HTX_STRM(__sc_strm(sc))))
struct sedesc *sedesc_new();
void sedesc_free(struct sedesc *sedesc);
+void se_shutdown(struct sedesc *sedesc, enum se_shut_mode mode);
+
struct stconn *sc_new_from_endp(struct sedesc *sedesc, struct session *sess, struct buffer *input);
struct stconn *sc_new_from_strm(struct stream *strm, unsigned int flags);
struct stconn *sc_new_from_check(struct check *check, unsigned int flags);
@@ -255,7 +257,7 @@ static inline void *__sc_mux_strm(const struct stconn *sc)
{
return __sc_endp(sc);
}
-static inline struct appctx *sc_mux_strm(const struct stconn *sc)
+static inline void *sc_mux_strm(const struct stconn *sc)
{
if (sc_ep_test(sc, SE_FL_T_MUX))
return __sc_mux_strm(sc);
@@ -318,54 +320,6 @@ static inline const char *sc_get_data_name(const struct stconn *sc)
return sc->app_ops->name;
}
-/* shut read */
-static inline void sc_conn_shutr(struct stconn *sc, enum co_shr_mode mode)
-{
- const struct mux_ops *mux;
-
- BUG_ON(!sc_conn(sc));
-
- if (sc_ep_test(sc, SE_FL_SHR))
- return;
-
- /* clean data-layer shutdown */
- mux = sc_mux_ops(sc);
- if (mux && mux->shutr)
- mux->shutr(sc, mode);
- sc_ep_set(sc, (mode == CO_SHR_DRAIN) ? SE_FL_SHRD : SE_FL_SHRR);
-}
-
-/* shut write */
-static inline void sc_conn_shutw(struct stconn *sc, enum co_shw_mode mode)
-{
- const struct mux_ops *mux;
-
- BUG_ON(!sc_conn(sc));
-
- if (sc_ep_test(sc, SE_FL_SHW))
- return;
-
- /* clean data-layer shutdown */
- mux = sc_mux_ops(sc);
- if (mux && mux->shutw)
- mux->shutw(sc, mode);
- sc_ep_set(sc, (mode == CO_SHW_NORMAL) ? SE_FL_SHWN : SE_FL_SHWS);
-}
-
-/* completely close a stream connector (but do not detach it) */
-static inline void sc_conn_shut(struct stconn *sc)
-{
- sc_conn_shutw(sc, CO_SHW_SILENT);
- sc_conn_shutr(sc, CO_SHR_RESET);
-}
-
-/* completely close a stream connector after draining possibly pending data (but do not detach it) */
-static inline void sc_conn_drain_and_shut(struct stconn *sc)
-{
- sc_conn_shutw(sc, CO_SHW_SILENT);
- sc_conn_shutr(sc, CO_SHR_DRAIN);
-}
-
/* Returns non-zero if the stream connector's Rx path is blocked because of
* lack of room in the input buffer. This usually happens after applets failed
* to deliver data into the channel's buffer and reported it via sc_need_room().
@@ -423,12 +377,15 @@ static inline void se_need_remote_conn(struct sedesc *se)
}
/* The application layer tells the stream connector that it just got the input
- * buffer it was waiting for. A read activity is reported.
+ * buffer it was waiting for. A read activity is reported. The SC_FL_HAVE_BUFF
+ * flag is set and held until sc_used_buff() is called to indicatee it was
+ * used.
*/
static inline void sc_have_buff(struct stconn *sc)
{
if (sc->flags & SC_FL_NEED_BUFF) {
sc->flags &= ~SC_FL_NEED_BUFF;
+ sc->flags |= SC_FL_HAVE_BUFF;
sc_ep_report_read_activity(sc);
}
}
@@ -443,6 +400,14 @@ static inline void sc_need_buff(struct stconn *sc)
sc->flags |= SC_FL_NEED_BUFF;
}
+/* The stream connector indicates that it has successfully allocated the buffer
+ * it was previously waiting for so it drops the SC_FL_HAVE_BUFF bit.
+ */
+static inline void sc_used_buff(struct stconn *sc)
+{
+ sc->flags &= ~SC_FL_HAVE_BUFF;
+}
+
/* Tell a stream connector some room was made in the input buffer and any
* failed attempt to inject data into it may be tried again. This is usually
* called after a successful transfer of buffer contents to the other side.
@@ -502,7 +467,7 @@ static inline void se_need_more_data(struct sedesc *se)
}
-static inline size_t se_nego_ff(struct sedesc *se, struct buffer *input, size_t count, unsigned int may_splice)
+static inline size_t se_nego_ff(struct sedesc *se, struct buffer *input, size_t count, unsigned int flags)
{
size_t ret = 0;
@@ -517,7 +482,7 @@ static inline size_t se_nego_ff(struct sedesc *se, struct buffer *input, size_t
goto end;
}
- ret = mux->nego_fastfwd(se->sc, input, count, may_splice);
+ ret = mux->nego_fastfwd(se->sc, input, count, flags);
if (se->iobuf.flags & IOBUF_FL_FF_BLOCKED) {
sc_ep_report_blocked_send(se->sc, 0);
@@ -537,21 +502,53 @@ static inline size_t se_nego_ff(struct sedesc *se, struct buffer *input, size_t
return ret;
}
-static inline void se_done_ff(struct sedesc *se)
+/* Returns the number of bytes forwarded. May be 0 if nothing is forwarded. It
+ * may also be 0 if there is nothing to forward. Note it is not dependent on
+ * data in the buffer but only on the amount of data to forward.
+ */
+static inline size_t se_done_ff(struct sedesc *se)
{
+ size_t ret = 0;
+
if (se_fl_test(se, SE_FL_T_MUX)) {
const struct mux_ops *mux = se->conn->mux;
- size_t sent, to_send = se_ff_data(se);
+ size_t to_send = se_ff_data(se);
BUG_ON(!mux->done_fastfwd);
- sent = mux->done_fastfwd(se->sc);
- if (to_send) {
- if (sent == to_send)
+ ret = mux->done_fastfwd(se->sc);
+ if (ret) {
+ /* Something was forwarded, unblock the zero-copy forwarding.
+ * If all data was sent, report and send activity.
+ * Otherwise report a conditional blocked send.
+ */
+ se->iobuf.flags &= ~IOBUF_FL_FF_BLOCKED;
+ if (ret == to_send)
sc_ep_report_send_activity(se->sc);
else
- sc_ep_report_blocked_send(se->sc, sent != 0);
+ sc_ep_report_blocked_send(se->sc, 1);
+ }
+ else {
+ /* Nothing was forwarded. If there was something to forward,
+ * it means the sends are blocked.
+ * In addition, if the zero-copy forwarding is blocked because the
+ * producer requests more room, we must subs for sends.
+ */
+ if (to_send)
+ sc_ep_report_blocked_send(se->sc, 0);
+ if (se->iobuf.flags & IOBUF_FL_FF_BLOCKED) {
+ sc_ep_report_blocked_send(se->sc, 0);
+
+ if (!(se->sc->wait_event.events & SUB_RETRY_SEND)) {
+ /* The SC must be subs for send to be notify when some
+ * space is made
+ */
+ mux->subscribe(se->sc, SUB_RETRY_SEND, &se->sc->wait_event);
+ }
+ }
}
}
+
+ return ret;
}
#endif /* _HAPROXY_STCONN_H */
diff --git a/include/haproxy/stick_table-t.h b/include/haproxy/stick_table-t.h
index 749cb9a..4b98439 100644
--- a/include/haproxy/stick_table-t.h
+++ b/include/haproxy/stick_table-t.h
@@ -58,7 +58,8 @@ enum {
STKTABLE_DT_GPT, /* array of gpt */
STKTABLE_DT_GPC, /* array of gpc */
STKTABLE_DT_GPC_RATE, /* array of gpc_rate */
-
+ STKTABLE_DT_GLITCH_CNT, /* cumulated number of front glitches */
+ STKTABLE_DT_GLITCH_RATE, /* rate of front glitches */
STKTABLE_STATIC_DATA_TYPES,/* number of types above */
/* up to STKTABLE_EXTRA_DATA_TYPES types may be registered here, always
@@ -118,7 +119,7 @@ union stktable_data {
unsigned long long std_t_ull;
struct freq_ctr std_t_frqp;
struct dict_entry *std_t_dict;
-};
+} __attribute__((packed, aligned(sizeof(int))));
/* known data types */
struct stktable_data_type {
@@ -146,7 +147,8 @@ struct stksess {
unsigned int expire; /* session expiration date */
unsigned int ref_cnt; /* reference count, can only purge when zero */
__decl_thread(HA_RWLOCK_T lock); /* lock related to the table entry */
- int shard; /* shard */
+ int shard; /* shard number used by peers */
+ int seen; /* 0 only when no peer has seen this entry yet */
struct eb32_node exp; /* ebtree node used to hold the session in expiration tree */
struct eb32_node upd; /* ebtree node used to hold the update sequence tree */
struct ebmb_node key; /* ebtree node used to hold the session in table */
@@ -196,8 +198,12 @@ struct stktable {
THREAD_ALIGN(64);
- struct eb_root keys; /* head of sticky session tree */
- struct eb_root exps; /* head of sticky session expiration tree */
+ struct {
+ struct eb_root keys; /* head of sticky session tree */
+ struct eb_root exps; /* head of sticky session expiration tree */
+ __decl_thread(HA_RWLOCK_T sh_lock); /* for the trees above */
+ } shards[CONFIG_HAP_TBL_BUCKETS];
+
unsigned int refcnt; /* number of local peer over all peers sections
attached to this table */
unsigned int current; /* number of sticky sessions currently in table */
diff --git a/include/haproxy/stick_table.h b/include/haproxy/stick_table.h
index 3200437..2c5e7a2 100644
--- a/include/haproxy/stick_table.h
+++ b/include/haproxy/stick_table.h
@@ -29,7 +29,9 @@
#include <haproxy/freq_ctr.h>
#include <haproxy/sample-t.h>
#include <haproxy/stick_table-t.h>
+#include <haproxy/thread.h>
#include <haproxy/ticks.h>
+#include <haproxy/xxhash.h>
extern struct stktable *stktables_list;
extern struct pool_head *pool_head_stk_ctr;
@@ -191,6 +193,19 @@ static inline void *stktable_data_ptr_idx(struct stktable *t, struct stksess *ts
return __stktable_data_ptr(t, ts, type) + idx*stktable_type_size(stktable_data_types[type].std_type);
}
+/* return a shard number for key <key> of len <len> present in table <t>, for
+ * use with the tree indexing. The value will be from 0 to
+ * CONFIG_HAP_TBL_BUCKETS-1.
+ */
+static inline uint stktable_calc_shard_num(const struct stktable *t, const void *key, size_t len)
+{
+#if CONFIG_HAP_TBL_BUCKETS > 1
+ return XXH32(key, len, t->hash_seed) % CONFIG_HAP_TBL_BUCKETS;
+#else
+ return 0;
+#endif
+}
+
/* kill an entry if it's expired and its ref_cnt is zero */
static inline int __stksess_kill_if_expired(struct stktable *t, struct stksess *ts)
{
@@ -202,14 +217,26 @@ static inline int __stksess_kill_if_expired(struct stktable *t, struct stksess *
static inline void stksess_kill_if_expired(struct stktable *t, struct stksess *ts, int decrefcnt)
{
+ uint shard;
+ size_t len;
if (decrefcnt && HA_ATOMIC_SUB_FETCH(&ts->ref_cnt, 1) != 0)
return;
if (t->expire != TICK_ETERNITY && tick_is_expired(ts->expire, now_ms)) {
- HA_RWLOCK_WRLOCK(STK_TABLE_LOCK, &t->lock);
+ if (t->type == SMP_T_STR)
+ len = strlen((const char *)ts->key.key);
+ else
+ len = t->key_size;
+
+ shard = stktable_calc_shard_num(t, ts->key.key, len);
+
+ /* make the compiler happy when shard is not used without threads */
+ ALREADY_CHECKED(shard);
+
+ HA_RWLOCK_WRLOCK(STK_TABLE_LOCK, &t->shards[shard].sh_lock);
__stksess_kill_if_expired(t, ts);
- HA_RWLOCK_WRUNLOCK(STK_TABLE_LOCK, &t->lock);
+ HA_RWLOCK_WRUNLOCK(STK_TABLE_LOCK, &t->shards[shard].sh_lock);
}
}
@@ -401,4 +428,36 @@ static inline int stkctr_inc_bytes_out_ctr(struct stkctr *stkctr, unsigned long
return 1;
}
+/* Add <inc> to the number of cumulated front glitches in the tracked counter
+ * <stkctr>. It returns 0 if the entry pointer does not exist and nothing is
+ * performed. Otherwise it returns 1.
+ */
+static inline int stkctr_add_glitch_ctr(struct stkctr *stkctr, uint inc)
+{
+ struct stksess *ts;
+ void *ptr1, *ptr2;
+
+ ts = stkctr_entry(stkctr);
+ if (!ts)
+ return 0;
+
+ HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
+
+ ptr1 = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_GLITCH_CNT);
+ if (ptr1)
+ stktable_data_cast(ptr1, std_t_uint) += inc;
+
+ ptr2 = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_GLITCH_RATE);
+ if (ptr2)
+ update_freq_ctr_period(&stktable_data_cast(ptr2, std_t_frqp),
+ stkctr->table->data_arg[STKTABLE_DT_GLITCH_RATE].u, inc);
+
+ HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
+
+ /* If data was modified, we need to touch to re-schedule sync */
+ if (ptr1 || ptr2)
+ stktable_touch_local(stkctr->table, ts, 0);
+ return 1;
+}
+
#endif /* _HAPROXY_STICK_TABLE_H */
diff --git a/include/haproxy/stream-t.h b/include/haproxy/stream-t.h
index 4280692..b96512e 100644
--- a/include/haproxy/stream-t.h
+++ b/include/haproxy/stream-t.h
@@ -40,7 +40,7 @@
*/
#define SF_DIRECT 0x00000001 /* connection made on the server matching the client cookie */
#define SF_ASSIGNED 0x00000002 /* no need to assign a server to this stream */
-/* unused: 0x00000004 */
+#define SF_MAYALLOC 0x00000004 /* we were notified that a work buffer might be available now */
#define SF_BE_ASSIGNED 0x00000008 /* a backend was assigned. Conns are accounted. */
#define SF_FORCE_PRST 0x00000010 /* force persistence here, even if server is down */
@@ -86,6 +86,8 @@
#define SF_SRV_REUSED_ANTICIPATED 0x00200000 /* the connection was reused but the mux is not ready yet */
#define SF_WEBSOCKET 0x00400000 /* websocket stream */ // TODO: must be removed
#define SF_SRC_ADDR 0x00800000 /* get the source ip/port with getsockname */
+#define SF_BC_MARK 0x01000000 /* need to set specific mark on backend/srv conn upon connect */
+#define SF_BC_TOS 0x02000000 /* need to set specific tos on backend/srv conn upon connect */
/* This function is used to report flags in debugging tools. Please reflect
* below any single-bit flag addition above in the same order via the
@@ -100,7 +102,7 @@ static forceinline char *strm_show_flags(char *buf, size_t len, const char *deli
_(0);
/* flags & enums */
_(SF_IGNORE_PRST, _(SF_SRV_REUSED, _(SF_SRV_REUSED_ANTICIPATED,
- _(SF_WEBSOCKET, _(SF_SRC_ADDR)))));
+ _(SF_WEBSOCKET, _(SF_SRC_ADDR, _(SF_BC_MARK, _(SF_BC_TOS)))))));
_e(SF_FINST_MASK, SF_FINST_R, _e(SF_FINST_MASK, SF_FINST_C,
_e(SF_FINST_MASK, SF_FINST_H, _e(SF_FINST_MASK, SF_FINST_D,
@@ -114,9 +116,9 @@ static forceinline char *strm_show_flags(char *buf, size_t len, const char *deli
_e(SF_ERR_MASK, SF_ERR_DOWN, _e(SF_ERR_MASK, SF_ERR_KILLED,
_e(SF_ERR_MASK, SF_ERR_UP, _e(SF_ERR_MASK, SF_ERR_CHK_PORT))))))))))));
- _(SF_DIRECT, _(SF_ASSIGNED, _(SF_BE_ASSIGNED, _(SF_FORCE_PRST,
+ _(SF_DIRECT, _(SF_ASSIGNED, _(SF_MAYALLOC, _(SF_BE_ASSIGNED, _(SF_FORCE_PRST,
_(SF_MONITOR, _(SF_CURR_SESS, _(SF_CONN_EXP, _(SF_REDISP,
- _(SF_IGNORE, _(SF_REDIRECTABLE, _(SF_HTX)))))))))));
+ _(SF_IGNORE, _(SF_REDIRECTABLE, _(SF_HTX))))))))))));
/* epilogue */
_(~0U);
@@ -209,6 +211,9 @@ struct stream {
int flags; /* some flags describing the stream */
unsigned int uniq_id; /* unique ID used for the traces */
+ uint32_t bc_mark; /* set mark on back conn if SF_BC_MARK is set */
+ uint8_t bc_tos; /* set tos on back conn if SF_BC_TOS is set */
+ /* 3 unused bytes here */
enum obj_type *target; /* target to use for this stream */
struct session *sess; /* the session this stream is attached to */
diff --git a/include/haproxy/stream.h b/include/haproxy/stream.h
index a884007..12c58b8 100644
--- a/include/haproxy/stream.h
+++ b/include/haproxy/stream.h
@@ -69,7 +69,7 @@ void stream_shutdown(struct stream *stream, int why);
void stream_dump_and_crash(enum obj_type *obj, int rate);
void strm_dump_to_buffer(struct buffer *buf, const struct stream *strm, const char *pfx, uint32_t anon_key);
-struct ist stream_generate_unique_id(struct stream *strm, struct list *format);
+struct ist stream_generate_unique_id(struct stream *strm, struct lf_expr *format);
void stream_process_counters(struct stream *s);
void sess_change_server(struct stream *strm, struct server *newsrv);
diff --git a/include/haproxy/systemd.h b/include/haproxy/systemd.h
new file mode 100644
index 0000000..65b0ab6
--- /dev/null
+++ b/include/haproxy/systemd.h
@@ -0,0 +1,7 @@
+#ifndef _HAPROXY_SYSTEMD_H
+#define _HAPROXY_SYSTEMD_H
+
+int sd_notify(int unset_environment, const char *message);
+int sd_notifyf(int unset_environment, const char *format, ...);
+
+#endif
diff --git a/include/haproxy/task-t.h b/include/haproxy/task-t.h
index ea52de9..b525420 100644
--- a/include/haproxy/task-t.h
+++ b/include/haproxy/task-t.h
@@ -164,6 +164,24 @@ struct tasklet {
*/
};
+/* Note: subscribing to these events is only valid after the caller has really
+ * attempted to perform the operation, and failed to proceed or complete.
+ */
+enum sub_event_type {
+ SUB_RETRY_RECV = 0x00000001, /* Schedule the tasklet when we can attempt to recv again */
+ SUB_RETRY_SEND = 0x00000002, /* Schedule the tasklet when we can attempt to send again */
+};
+
+/* Describes a set of subscriptions. Multiple events may be registered at the
+ * same time. The callee should assume everything not pending for completion is
+ * implicitly possible. It's illegal to change the tasklet if events are still
+ * registered.
+ */
+struct wait_event {
+ struct tasklet *tasklet;
+ int events; /* set of enum sub_event_type above */
+};
+
/*
* The task callback (->process) is responsible for updating ->expire. It must
* return a pointer to the task itself, except if the task has been deleted, in
diff --git a/include/haproxy/tcpcheck-t.h b/include/haproxy/tcpcheck-t.h
index 8878995..22310ee 100644
--- a/include/haproxy/tcpcheck-t.h
+++ b/include/haproxy/tcpcheck-t.h
@@ -134,9 +134,9 @@ struct tcpcheck_connect {
};
struct tcpcheck_http_hdr {
- struct ist name; /* the header name */
- struct list value; /* the log-format string value */
- struct list list; /* header chained list */
+ struct ist name; /* the header name */
+ struct lf_expr value; /* the log-format string value */
+ struct list list; /* header linked list */
};
struct tcpcheck_codes {
@@ -147,20 +147,20 @@ struct tcpcheck_codes {
struct tcpcheck_send {
enum tcpcheck_send_type type;
union {
- struct ist data; /* an ASCII string or a binary sequence */
- struct list fmt; /* an ASCII or hexa log-format string */
+ struct ist data; /* an ASCII string or a binary sequence */
+ struct lf_expr fmt; /* an ASCII or hexa log-format string */
struct {
unsigned int flags; /* TCPCHK_SND_HTTP_FL_* */
struct http_meth meth; /* the HTTP request method */
union {
struct ist uri; /* the HTTP request uri is a string */
- struct list uri_fmt; /* or a log-format string */
+ struct lf_expr uri_fmt; /* or a log-format string */
};
struct ist vsn; /* the HTTP request version string */
struct list hdrs; /* the HTTP request header list */
union {
struct ist body; /* the HTTP request payload is a string */
- struct list body_fmt; /* or a log-format string */
+ struct lf_expr body_fmt;/* or a log-format string */
};
} http; /* Info about the HTTP request to send */
};
@@ -173,16 +173,16 @@ struct tcpcheck_expect {
struct ist data; /* Matching a literal string / binary anywhere in the response. */
struct my_regex *regex; /* Matching a regex pattern. */
struct tcpcheck_codes codes; /* Matching a list of codes */
- struct list fmt; /* Matching a log-format string / binary */
+ struct lf_expr fmt; /* Matching a log-format string / binary */
struct {
union {
struct ist name;
- struct list name_fmt;
+ struct lf_expr name_fmt;
struct my_regex *name_re;
};
union {
struct ist value;
- struct list value_fmt;
+ struct lf_expr value_fmt;
struct my_regex *value_re;
};
} hdr; /* Matching a header pattern */
@@ -196,9 +196,9 @@ struct tcpcheck_expect {
enum healthcheck_status ok_status; /* The healthcheck status to use on success (default: L7OKD) */
enum healthcheck_status err_status; /* The healthcheck status to use on error (default: L7RSP) */
enum healthcheck_status tout_status; /* The healthcheck status to use on timeout (default: L7TOUT) */
- struct list onerror_fmt; /* log-format string to use as comment on error */
- struct list onsuccess_fmt; /* log-format string to use as comment on success (if last rule) */
- struct sample_expr *status_expr; /* sample expr to determine the check status code */
+ struct lf_expr onerror_fmt; /* log-format string to use as comment on error */
+ struct lf_expr onsuccess_fmt; /* log-format string to use as comment on success (if last rule) */
+ struct sample_expr *status_expr; /* sample expr to determine the check status code */
};
struct tcpcheck_action_kw {
diff --git a/include/haproxy/tcpcheck.h b/include/haproxy/tcpcheck.h
index 3abd1ef..55c564a 100644
--- a/include/haproxy/tcpcheck.h
+++ b/include/haproxy/tcpcheck.h
@@ -83,6 +83,10 @@ struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, struct pro
struct list *rules, unsigned int proto,
const char *file, int line, char **errmsg);
+int proxy_parse_tcpcheck(char **args, int section, struct proxy *curpx,
+ const struct proxy *defpx, const char *file, int line,
+ char **errmsg);
+
int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, const struct proxy *defpx,
const char *file, int line);
int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, const struct proxy *defpx,
diff --git a/include/haproxy/thread-t.h b/include/haproxy/thread-t.h
index f3552c2..5d36107 100644
--- a/include/haproxy/thread-t.h
+++ b/include/haproxy/thread-t.h
@@ -162,4 +162,59 @@ struct ha_rwlock {
#endif /* DEBUG_THREAD */
+/* WARNING!!! if you update this enum, please also keep lock_label() up to date
+ * below.
+ */
+enum lock_label {
+ TASK_RQ_LOCK,
+ TASK_WQ_LOCK,
+ LISTENER_LOCK,
+ PROXY_LOCK,
+ SERVER_LOCK,
+ LBPRM_LOCK,
+ SIGNALS_LOCK,
+ STK_TABLE_LOCK,
+ STK_SESS_LOCK,
+ APPLETS_LOCK,
+ PEER_LOCK,
+ SHCTX_LOCK,
+ SSL_LOCK,
+ SSL_GEN_CERTS_LOCK,
+ PATREF_LOCK,
+ PATEXP_LOCK,
+ VARS_LOCK,
+ COMP_POOL_LOCK,
+ LUA_LOCK,
+ NOTIF_LOCK,
+ SPOE_APPLET_LOCK,
+ DNS_LOCK,
+ PID_LIST_LOCK,
+ EMAIL_ALERTS_LOCK,
+ PIPES_LOCK,
+ TLSKEYS_REF_LOCK,
+ AUTH_LOCK,
+ RING_LOCK,
+ DICT_LOCK,
+ PROTO_LOCK,
+ QUEUE_LOCK,
+ CKCH_LOCK,
+ SNI_LOCK,
+ SSL_SERVER_LOCK,
+ SFT_LOCK, /* sink forward target */
+ IDLE_CONNS_LOCK,
+ OCSP_LOCK,
+ QC_CID_LOCK,
+ CACHE_LOCK,
+ OTHER_LOCK,
+ /* WT: make sure never to use these ones outside of development,
+ * we need them for lock profiling!
+ */
+ DEBUG1_LOCK,
+ DEBUG2_LOCK,
+ DEBUG3_LOCK,
+ DEBUG4_LOCK,
+ DEBUG5_LOCK,
+ LOCK_LABELS
+};
+
#endif /* _HAPROXY_THREAD_T_H */
diff --git a/include/haproxy/thread.h b/include/haproxy/thread.h
index 8c7520b..0984c67 100644
--- a/include/haproxy/thread.h
+++ b/include/haproxy/thread.h
@@ -386,62 +386,6 @@ int thread_cpu_mask_forced(void);
#define HA_RWLOCK_TRYSKLOCK(lbl,l) __ha_rwlock_trysklock(lbl, l, __func__, __FILE__, __LINE__)
#define HA_RWLOCK_TRYRDTOSK(lbl,l) __ha_rwlock_tryrdtosk(lbl, l, __func__, __FILE__, __LINE__)
-/* WARNING!!! if you update this enum, please also keep lock_label() up to date
- * below.
- */
-enum lock_label {
- TASK_RQ_LOCK,
- TASK_WQ_LOCK,
- LISTENER_LOCK,
- PROXY_LOCK,
- SERVER_LOCK,
- LBPRM_LOCK,
- SIGNALS_LOCK,
- STK_TABLE_LOCK,
- STK_SESS_LOCK,
- APPLETS_LOCK,
- PEER_LOCK,
- SHCTX_LOCK,
- SSL_LOCK,
- SSL_GEN_CERTS_LOCK,
- PATREF_LOCK,
- PATEXP_LOCK,
- VARS_LOCK,
- COMP_POOL_LOCK,
- LUA_LOCK,
- NOTIF_LOCK,
- SPOE_APPLET_LOCK,
- DNS_LOCK,
- PID_LIST_LOCK,
- EMAIL_ALERTS_LOCK,
- PIPES_LOCK,
- TLSKEYS_REF_LOCK,
- AUTH_LOCK,
- RING_LOCK,
- DICT_LOCK,
- PROTO_LOCK,
- QUEUE_LOCK,
- CKCH_LOCK,
- SNI_LOCK,
- SSL_SERVER_LOCK,
- SFT_LOCK, /* sink forward target */
- IDLE_CONNS_LOCK,
- OCSP_LOCK,
- QC_CID_LOCK,
- CACHE_LOCK,
- OTHER_LOCK,
- /* WT: make sure never to use these ones outside of development,
- * we need them for lock profiling!
- */
- DEBUG1_LOCK,
- DEBUG2_LOCK,
- DEBUG3_LOCK,
- DEBUG4_LOCK,
- DEBUG5_LOCK,
- LOCK_LABELS
-};
-
-
/* Following functions are used to collect some stats about locks. We wrap
* pthread functions to known how much time we wait in a lock. */
diff --git a/include/haproxy/tinfo-t.h b/include/haproxy/tinfo-t.h
index 357c4c0..636d5b2 100644
--- a/include/haproxy/tinfo-t.h
+++ b/include/haproxy/tinfo-t.h
@@ -65,6 +65,8 @@ enum {
#define TH_FL_STARTED 0x00000010 /* set once the thread starts */
#define TH_FL_IN_LOOP 0x00000020 /* set only inside the polling loop */
+/* we have 4 buffer-wait queues, in highest to lowest emergency order */
+#define DYNBUF_NBQ 4
/* Thread group information. This defines a base and a count of global thread
* IDs which belong to it, and which can be looked up into thread_info/ctx. It
@@ -110,7 +112,7 @@ struct thread_info {
uint tid, ltid; /* process-wide and group-wide thread ID (start at 0) */
ulong ltid_bit; /* bit masks for the tid/ltid */
uint tgid; /* ID of the thread group this thread belongs to (starts at 1; 0=unset) */
- /* 32-bit hole here */
+ uint ring_queue; /* queue number for the rings */
ullong pth_id; /* the pthread_t cast to a ullong */
void *stack_top; /* the top of the stack when entering the thread */
@@ -133,19 +135,25 @@ struct thread_ctx {
int current_queue; /* points to current tasklet list being run, -1 if none */
unsigned int nb_tasks; /* number of tasks allocated on this thread */
uint8_t tl_class_mask; /* bit mask of non-empty tasklets classes */
+ uint8_t bufq_map; /* one bit per non-empty buffer_wq */
- // 7 bytes hole here
+ // 2 bytes hole here
+ unsigned int nb_rhttp_conns; /* count of current conns used for active reverse HTTP */
+ struct sched_activity *sched_profile_entry; /* profile entry in use by the current task/tasklet, only if sched_wake_date>0 */
+
+ ALWAYS_ALIGN(2*sizeof(void*));
+ struct list buffer_wq[DYNBUF_NBQ]; /* buffer waiters, 4 criticality-based queues */
struct list pool_lru_head; /* oldest objects in thread-local pool caches */
- struct list buffer_wq; /* buffer waiters */
struct list streams; /* list of streams attached to this thread */
struct list quic_conns; /* list of active quic-conns attached to this thread */
struct list quic_conns_clo; /* list of closing quic-conns attached to this thread */
struct list queued_checks; /* checks waiting for a connection slot */
- unsigned int nb_rhttp_conns; /* count of current conns used for active reverse HTTP */
-
- ALWAYS_ALIGN(2*sizeof(void*));
struct list tasklets[TL_CLASSES]; /* tasklets (and/or tasks) to run, by class */
+ void **emergency_bufs; /* array of buffers allocated at boot. Next free one is [emergency_bufs_left-1] */
+ uint emergency_bufs_left; /* number of emergency buffers left in magic_bufs[] */
+ // around 36 bytes here for thread-local variables
+
// third cache line here on 64 bits: accessed mostly using atomic ops
ALWAYS_ALIGN(64);
struct mt_list shared_tasklet_list; /* Tasklet to be run, woken up by other threads */
@@ -158,7 +166,6 @@ struct thread_ctx {
uint32_t sched_wake_date; /* current task/tasklet's wake date or 0 */
uint32_t sched_call_date; /* current task/tasklet's call date (valid if sched_wake_date > 0) */
- struct sched_activity *sched_profile_entry; /* profile entry in use by the current task/tasklet, only if sched_wake_date>0 */
uint64_t prev_cpu_time; /* previous per thread CPU time */
uint64_t prev_mono_time; /* previous system wide monotonic time */
@@ -172,6 +179,7 @@ struct thread_ctx {
unsigned long long out_bytes; /* total #of bytes emitted */
unsigned long long spliced_out_bytes; /* total #of bytes emitted though a kernel pipe */
struct buffer *thread_dump_buffer; /* NULL out of dump, valid during a dump, 0x01 once done */
+ // around 64 bytes here for shared variables
ALWAYS_ALIGN(128);
};
diff --git a/include/haproxy/tools-t.h b/include/haproxy/tools-t.h
index 32d8193..a63e0f6 100644
--- a/include/haproxy/tools-t.h
+++ b/include/haproxy/tools-t.h
@@ -22,6 +22,8 @@
#ifndef _HAPROXY_TOOLS_T_H
#define _HAPROXY_TOOLS_T_H
+#include <netinet/in.h>
+
/* size used for max length of decimal representation of long long int. */
#define NB_LLMAX_STR (sizeof("-9223372036854775807")-1)
@@ -163,4 +165,20 @@ struct net_addr_type {
int xprt_type; // transport layer
};
+/* To easily pass context to cbor encode functions
+ */
+struct cbor_encode_ctx {
+ /* function pointer that cbor encode functions will use to encode a
+ * single byte.
+ *
+ * The function needs to return the position of the last written byte
+ * on success and NULL on failure. The function cannot write past <stop>
+ */
+ char *(*e_fct_byte)(struct cbor_encode_ctx *ctx,
+ char *start, char *stop, uint8_t byte);
+
+ /* to provide some user-context to the encode_fct_* funcs */
+ void *e_fct_ctx;
+};
+
#endif /* _HAPROXY_TOOLS_T_H */
diff --git a/include/haproxy/tools.h b/include/haproxy/tools.h
index 3726f63..937adaa 100644
--- a/include/haproxy/tools.h
+++ b/include/haproxy/tools.h
@@ -42,6 +42,7 @@
#include <haproxy/api.h>
#include <haproxy/chunk.h>
#include <haproxy/intops.h>
+#include <haproxy/global.h>
#include <haproxy/namespace-t.h>
#include <haproxy/protocol-t.h>
#include <haproxy/tools-t.h>
@@ -399,11 +400,11 @@ int addr_is_local(const struct netns_entry *ns,
* <map> with the hexadecimal representation of their ASCII-code (2 digits)
* prefixed by <escape>, and will store the result between <start> (included)
* and <stop> (excluded), and will always terminate the string with a '\0'
- * before <stop>. The position of the '\0' is returned if the conversion
- * completes. If bytes are missing between <start> and <stop>, then the
- * conversion will be incomplete and truncated. If <stop> <= <start>, the '\0'
- * cannot even be stored so we return <start> without writing the 0.
+ * before <stop>. If bytes are missing between <start> and <stop>, then the
+ * conversion will be incomplete and truncated.
* The input string must also be zero-terminated.
+ *
+ * Return the address of the \0 character, or NULL on error
*/
extern const char hextab[];
extern long query_encode_map[];
@@ -424,13 +425,33 @@ char *encode_chunk(char *start, char *stop,
* is reached or NULL-byte is encountered. The result will
* be stored between <start> (included) and <stop> (excluded). This
* function will always try to terminate the resulting string with a '\0'
- * before <stop>, and will return its position if the conversion
- * completes.
+ * before <stop>.
+ *
+ * Return the address of the \0 character, or NULL on error
*/
char *escape_string(char *start, char *stop,
const char escape, const long *map,
const char *string, const char *string_stop);
+/* Below are RFC8949 compliant cbor encode helper functions, see source
+ * file for functions descriptions
+ */
+char *cbor_encode_uint64_prefix(struct cbor_encode_ctx *ctx,
+ char *start, char *stop,
+ uint64_t value, uint8_t prefix);
+char *cbor_encode_int64(struct cbor_encode_ctx *ctx,
+ char *start, char *stop, int64_t value);
+char *cbor_encode_bytes_prefix(struct cbor_encode_ctx *ctx,
+ char *start, char *stop,
+ const char *bytes, size_t len,
+ uint8_t prefix);
+char *cbor_encode_bytes(struct cbor_encode_ctx *ctx,
+ char *start, char *stop,
+ const char *bytes, size_t len);
+char *cbor_encode_text(struct cbor_encode_ctx *ctx,
+ char *start, char *stop,
+ const char *text, size_t len);
+
/* Check a string for using it in a CSV output format. If the string contains
* one of the following four char <">, <,>, CR or LF, the string is
* encapsulated between <"> and the <"> are escaped by a <""> sequence.
@@ -761,6 +782,21 @@ static inline int set_host_port(struct sockaddr_storage *addr, int port)
return 0;
}
+/* Returns true if <addr> port is forbidden as client source using <proto>. */
+static inline int port_is_restricted(const struct sockaddr_storage *addr,
+ enum ha_proto proto)
+{
+ const uint16_t port = get_host_port(addr);
+
+ BUG_ON_HOT(proto != HA_PROTO_TCP && proto != HA_PROTO_QUIC);
+
+ /* RFC 6335 6. Port Number Ranges */
+ if (unlikely(port < 1024 && port > 0))
+ return !(global.clt_privileged_ports & proto);
+
+ return 0;
+}
+
/* Convert mask from bit length form to in_addr form.
* This function never fails.
*/
@@ -1006,6 +1042,8 @@ int dump_binary(struct buffer *out, const char *buf, int bsize);
int dump_text_line(struct buffer *out, const char *buf, int bsize, int len,
int *line, int ptr);
void dump_addr_and_bytes(struct buffer *buf, const char *pfx, const void *addr, int n);
+void dump_area_with_syms(struct buffer *output, const void *base, const void *addr,
+ const void *special, const char *spec_type, const char *spec_name);
void dump_hex(struct buffer *out, const char *pfx, const void *buf, int len, int unsafe);
int may_access(const void *ptr);
const void *resolve_sym_name(struct buffer *buf, const char *pfx, const void *addr);
@@ -1053,7 +1091,8 @@ static inline void *my_realloc2(void *ptr, size_t size)
int parse_dotted_uints(const char *s, unsigned int **nums, size_t *sz);
/* PRNG */
-void ha_generate_uuid(struct buffer *output);
+void ha_generate_uuid_v4(struct buffer *output);
+void ha_generate_uuid_v7(struct buffer *output);
void ha_random_seed(const unsigned char *seed, size_t len);
void ha_random_jump96(uint32_t dist);
uint64_t ha_random64(void);
@@ -1176,4 +1215,8 @@ int openssl_compare_current_version(const char *version);
/* compare the current OpenSSL name to a string */
int openssl_compare_current_name(const char *name);
+/* vma helpers */
+void vma_set_name(void *addr, size_t size, const char *type, const char *name);
+void vma_set_name_id(void *addr, size_t size, const char *type, const char *name, unsigned int id);
+
#endif /* _HAPROXY_TOOLS_H */
diff --git a/include/haproxy/vars.h b/include/haproxy/vars.h
index ebd1f15..9fa351c 100644
--- a/include/haproxy/vars.h
+++ b/include/haproxy/vars.h
@@ -25,15 +25,20 @@
#include <haproxy/api-t.h>
#include <haproxy/session-t.h>
#include <haproxy/stream-t.h>
+#include <haproxy/thread.h>
#include <haproxy/vars-t.h>
extern struct vars proc_vars;
+struct sample;
+struct arg;
void vars_init_head(struct vars *vars, enum vars_scope scope);
void var_accounting_diff(struct vars *vars, struct session *sess, struct stream *strm, int size);
unsigned int var_clear(struct var *var, int force);
void vars_prune(struct vars *vars, struct session *sess, struct stream *strm);
void vars_prune_per_sess(struct vars *vars);
+int var_set(uint64_t name_hash, enum vars_scope scope, struct sample *smp, uint flags);
+int var_unset(uint64_t name_hash, enum vars_scope scope, struct sample *smp);
int vars_get_by_name(const char *name, size_t len, struct sample *smp, const struct buffer *def);
int vars_set_by_name_ifexist(const char *name, size_t len, struct sample *smp);
int vars_set_by_name(const char *name, size_t len, struct sample *smp);
diff --git a/include/haproxy/vecpair.h b/include/haproxy/vecpair.h
new file mode 100644
index 0000000..e495706
--- /dev/null
+++ b/include/haproxy/vecpair.h
@@ -0,0 +1,588 @@
+/*
+ * include/haproxy/vecpair.h
+ * Vector pair handling - functions definitions.
+ *
+ * Copyright (C) 2000-2024 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _HAPROXY_VECPAIR_H
+#define _HAPROXY_VECPAIR_H
+
+#include <sys/types.h>
+#include <string.h>
+#include <import/ist.h>
+#include <haproxy/api.h>
+
+
+/* Principles of operation
+ * -----------------------
+ * These functions take two vectors represented as ISTs, they're each the
+ * pointer to and the length of a work area. Functions operate over these
+ * two areas as if they were a contiguous area. It is up to the caller to
+ * use them to designate free space or data depending on whether it wants
+ * to write or read to the area. This allows to easily represent a wrapping
+ * buffer, both for data and free space.
+ *
+ * In order to ease sequencing of operations, most of the functions below
+ * will:
+ * - always consider v1 before v2
+ * - always ignore any vector whose length is zero (the pointer is ignored)
+ * - automatically switch from v1 to v2 upon updates, including if their
+ * size is zero
+ * - end after both v1 and v2 are depleted (len==0)
+ * - update the affected vectors after operation (pointer, length) so that
+ * they can easily be chained without adding new tests
+ * - return the number of bytes processed after operation.
+ *
+ * These functions do not need to know the allocated size nor any such thing,
+ * it's the caller's job to know that and to build the relevant vector pair.
+ * See the vp_{ring,data,room}_to_{ring,data,room}() functions at the end for
+ * this.
+ */
+
+/* vp_isempty(): returns true if both areas are empty */
+static inline int vp_isempty(const struct ist v1, const struct ist v2)
+{
+ return !v1.len && !v2.len;
+}
+
+/* vp_size(): returns the total size of the two vectors */
+static inline size_t vp_size(const struct ist v1, const struct ist v2)
+{
+ return v1.len + v2.len;
+}
+
+/* _vp_head() : returns the pointer to the head (beginning) of the area, which is
+ * the address of the first byte of the first non-empty area. It must not be
+ * called with both areas empty.
+ */
+static inline char *_vp_head(const struct ist v1, const struct ist v2)
+{
+ return v1.len ? v1.ptr : v2.ptr;
+}
+
+/* vp_head() : returns the pointer to the head (beginning) of the area, which is
+ * the address of the first byte of the first non-empty area. It may return
+ * NULL if both areas are empty.
+ */
+static inline char *vp_head(const struct ist v1, const struct ist v2)
+{
+ return v1.len ? v1.ptr : v2.len ? v2.ptr : NULL;
+}
+
+/* _vp_addr() : return the address corresponding to applying an offset <ofs>
+ * after the head. It must not be called with an offset larger than the total
+ * area size.
+ */
+static inline char *_vp_addr(const struct ist v1, const struct ist v2, size_t ofs)
+{
+ if (ofs < v1.len)
+ return v1.ptr + ofs;
+ else {
+ ofs -= v1.len;
+ return v2.ptr + ofs;
+ }
+}
+
+/* vp_addr() : return the address corresponding to applying an offset <ofs>
+ * after the head. It may return NULL if the length is beyond the total area
+ * size.
+ */
+static inline char *vp_addr(const struct ist v1, const struct ist v2, size_t ofs)
+{
+ if (ofs < v1.len)
+ return v1.ptr + ofs;
+ else {
+ ofs -= v1.len;
+ if (ofs >= v2.len)
+ return NULL;
+ return v2.ptr + ofs;
+ }
+}
+
+/* vp_ofs() : return the offset corresponding to the pointer <p> within either
+ * v1 or v2, or a size equal to the sum of both lengths if <p> is outside both
+ * areas.
+ */
+static inline size_t vp_ofs(const struct ist v1, const struct ist v2, const char *p)
+{
+ if (p >= v1.ptr && p < v1.ptr + v1.len)
+ return p - v1.ptr;
+
+ if (p >= v2.ptr && p < v2.ptr + v2.len)
+ return v1.len + (p - v2.ptr);
+
+ return v1.len + v2.len;
+}
+
+/* vp_next() : return the address of the next character after <p> or NULL if it
+ * runs out of both v1 and v2.
+ */
+static inline char *vp_next(const struct ist v1, const struct ist v2, const char *p)
+{
+ size_t ofs = vp_ofs(v1, v2, p);
+
+ return vp_addr(v1, v2, ofs + 1);
+}
+
+/* vp_seek_addr() : return the pointer to the byte at relative offset <seek> in
+ * the area(s). The caller must ensure that seek is strictly smaller than the
+ * total amount of bytes in the vectors.
+ */
+static inline char *vp_seek_addr(struct ist v1, struct ist v2, size_t seek)
+{
+ if (seek < v1.len)
+ return v1.ptr + seek;
+ else
+ return v2.ptr + seek - v1.len;
+}
+
+/*********************************************/
+/* Functions used to modify the buffer state */
+/*********************************************/
+
+/* vp_skip() : skip the requested amount of bytes from the area(s) and update
+ * them accordingly. If the amount to skip exceeds the total size of the two
+ * areas, they're emptied and the total number of emptied bytes is returned.
+ * It is unspecified what area pointers point to after their len is emptied.
+ */
+static inline size_t vp_skip(struct ist *v1, struct ist *v2, size_t skip)
+{
+ if (skip <= v1->len) {
+ v1->ptr += skip;
+ v1->len -= skip;
+ }
+ else {
+ if (skip > v1->len + v2->len)
+ skip = v1->len + v2->len;
+
+ v2->ptr += skip - v1->len;
+ v2->len -= skip - v1->len;
+ v1->ptr += v1->len;
+ v1->len = 0;
+ }
+ return skip;
+}
+
+/* vp_getchr() : tries to retrieve the next from the beginning of the area, and
+ * advance the beginning by one char on success. An int equal to the unsigned
+ * char is returned on success, otherwise a negative value if there is nothing
+ * left in the area.
+ */
+static inline int vp_getchr(struct ist *v1, struct ist *v2)
+{
+ int c = -1;
+
+ if (v1->len) {
+ v1->len--;
+ c = (unsigned char)*(v1->ptr++);
+ }
+ else if (v2->len) {
+ v2->len--;
+ c = (unsigned char)*(v2->ptr++);
+ }
+
+ return c;
+}
+
+/* vp_getblk_ofs() : gets one full block of data at once from a pair of vectors,
+ * starting from offset <ofs> after the head, and for up to <len> bytes. The
+ * caller is responsible for ensuring that <ofs> does not exceed the total
+ * number of bytes available in the areas. The areas will then be updated so
+ * that the next head points to the first unread byte (i.e. skip <ofs> plus
+ * the number of bytes returned). The number of bytes copied is returned. This
+ * is meant to be used on concurrently accessed areas, so that a reader can
+ * read a known area while it is been concurrently fed and/or trimmed. Usually
+ * you'd prefer to use the more convenient vp_getblk() or vp_peek_ofs().
+ */
+static inline size_t vp_getblk_ofs(struct ist *v1, struct ist *v2, size_t ofs, char *blk, size_t len)
+{
+ size_t ret = 0;
+ size_t block;
+
+ BUG_ON_HOT(ofs >= v1->len + v2->len);
+
+ vp_skip(v1, v2, ofs);
+
+ block = v1->len;
+ if (block > len)
+ block = len;
+
+ if (block) {
+ memcpy(blk + ret, v1->ptr, block);
+ v1->ptr += block;
+ v1->len -= block;
+ ret += block;
+ len -= block;
+ }
+
+ block = v2->len;
+ if (block > len)
+ block = len;
+
+ if (block) {
+ memcpy(blk + ret, v2->ptr, block);
+ v2->ptr += block;
+ v2->len -= block;
+ ret += block;
+ }
+
+ return ret;
+}
+
+/* vp_getblk() : gets one full block of data at once from a pair of vectors,
+ * starting from their head, and for up to <len> bytes. The areas will be
+ * updated so that the next head points to the first unread byte. The number
+ * of bytes copied is returned. This is meant to be used on concurrently
+ * accessed areas, so that a reader can read a known area while it is been
+ * concurrently fed and/or trimmed. See also vp_peek_ofs().
+ */
+static inline size_t vp_getblk(struct ist *v1, struct ist *v2, char *blk, size_t len)
+{
+ return vp_getblk_ofs(v1, v2, 0, blk, len);
+}
+
+/* vp_peek() : gets one full block of data at once from a pair of vectors,
+ * starting from offset <ofs> after the head, and for up to <len> bytes.
+ * The caller is responsible for ensuring that <ofs> does not exceed the
+ * total number of bytes available in the areas. The areas are *not* updated.
+ * The number of bytes copied is returned. This is meant to be used on
+ * concurrently accessed areas, so that a reader can read a known area while
+ * it is been concurrently fed and/or trimmed. See also vp_getblk().
+ */
+static inline size_t vp_peek_ofs(struct ist v1, struct ist v2, size_t ofs, char *blk, size_t len)
+{
+ return vp_getblk_ofs(&v1, &v2, ofs, blk, len);
+}
+
+/* vp_putchr() : tries to append char <c> at the beginning of the area, and
+ * advance the beginning by one char. Data are truncated if there is no room
+ * left.
+ */
+static inline void vp_putchr(struct ist *v1, struct ist *v2, char c)
+{
+ if (v1->len) {
+ v1->len--;
+ *(v1->ptr++) = c;
+ }
+ else if (v2->len) {
+ v2->len--;
+ *(v2->ptr++) = c;
+ }
+}
+
+/* vp_putblk_ofs() : put one full block of data at once into a pair of vectors,
+ * starting from offset <ofs> after the head, and for exactly <len> bytes.
+ * The caller is responsible for ensuring that <ofs> does not exceed the total
+ * number of bytes available in the areas. The function will check that it is
+ * indeed possible to put <len> bytes after <ofs> before proceeding. If the
+ * areas can accept such data, they will then be updated so that the next
+ * head points to the first untouched byte (i.e. skip <ofs> plus the number
+ * of bytes sent). The number of bytes copied is returned on success, or 0 is
+ * returned if it cannot be copied, in which case the areas are left
+ * untouched. This is meant to be used on concurrently accessed areas, so that
+ * a reader can read a known area while it is been concurrently fed and/or
+ * trimmed. Usually you'd prefer to use the more convenient vp_putblk() or
+ * vp_poke_ofs().
+ */
+static inline size_t vp_putblk_ofs(struct ist *v1, struct ist *v2, size_t ofs, const char *blk, size_t len)
+{
+ size_t ret = 0;
+ size_t block;
+
+ BUG_ON_HOT(ofs >= v1->len + v2->len);
+
+ if (len && ofs + len <= v1->len + v2->len) {
+ vp_skip(v1, v2, ofs);
+
+ block = v1->len;
+ if (block > len)
+ block = len;
+
+ if (block) {
+ memcpy(v1->ptr, blk + ret, block);
+ v1->ptr += block;
+ v1->len -= block;
+ ret += block;
+ len -= block;
+ }
+
+ block = v2->len;
+ if (block > len)
+ block = len;
+
+ if (block) {
+ memcpy(v2->ptr, blk + ret, block);
+ v2->ptr += block;
+ v2->len -= block;
+ ret += block;
+ }
+ }
+ return ret;
+}
+
+/* vp_pokeblk() : puts one full block of data at once into a pair of vectors,
+ * starting from offset <ofs> after the head, and for exactly <len> bytes.
+ * The caller is responsible for ensuring that neither <ofs> nor <ofs> + <len>
+ * exceed the total number of bytes available in the areas. This is meant to
+ * be used on concurrently accessed areas, so that a reader can read a known
+ * area while* it is been concurrently fed and/or trimmed. The area pointers
+ * are left unaffected. The number of bytes copied is returned.
+ */
+static inline size_t vp_poke_ofs(struct ist v1, struct ist v2, size_t ofs, const char *blk, size_t len)
+{
+ return vp_putblk_ofs(&v1, &v2, ofs, blk, len);
+}
+
+/* vp_putblk() : put one full block of data at once into a pair of vectors,
+ * starting at the head, and for exactly <len> bytes. The caller is
+ * responsible for ensuring that <len> does not exceed the total number of
+ * bytes available in the areas. This is meant to be used on concurrently
+ * accessed areas, so that a reader can read a known area while it is been
+ * concurrently fed and/or trimmed. The area pointers are updated according to
+ * the amount of bytes copied. The number of bytes copied is returned.
+ */
+static inline size_t vp_putblk(struct ist *v1, struct ist *v2, const char *blk, size_t len)
+{
+ vp_putblk_ofs(v1, v2, 0, blk, len);
+ return len;
+}
+
+/* vp_put_varint_ofs(): encode 64-bit value <v> as a varint into a pair of
+ * vectors, starting at an offset after the head. The code assumes that the
+ * caller has checked that the encoded value fits in the areas so that there
+ * are no length checks inside the loop. Vectors are updated and the number of
+ * written bytes is returned (excluding the offset).
+ */
+static inline size_t vp_put_varint_ofs(struct ist *v1, struct ist *v2, size_t ofs, uint64_t v)
+{
+ size_t data = 0;
+
+ BUG_ON_HOT(ofs >= v1->len + v2->len);
+
+ vp_skip(v1, v2, ofs);
+
+ if (v >= 0xF0) {
+ /* more than one byte, first write the 4 least significant
+ * bits, then follow with 7 bits per byte.
+ */
+ vp_putchr(v1, v2, v | 0xF0);
+ v = (v - 0xF0) >> 4;
+
+ while (1) {
+ data++;
+ if (v < 0x80)
+ break;
+ vp_putchr(v1, v2, v | 0x80);
+ v = (v - 0x80) >> 7;
+ }
+ }
+
+ /* last byte */
+ vp_putchr(v1, v2, v);
+ data++;
+ return data;
+}
+
+/* vp_put_varint(): encode 64-bit value <v> as a varint into a pair of vectors,
+ * starting at the head. The code assumes that the caller has checked that
+ * the encoded value fits in the areas so that there are no length checks
+ * inside the loop. Vectors are updated and the number of written bytes is
+ * returned.
+ */
+static inline size_t vp_put_varint(struct ist *v1, struct ist *v2, uint64_t v)
+{
+ return vp_put_varint_ofs(v1, v2, 0, v);
+}
+
+/* vp_get_varint_ofs(): try to decode a varint from a pair of vectors, starting
+ * at offset <ofs> after the head, into value <vptr>. Returns the number of
+ * bytes parsed in case of success, or 0 if there were not enough bytes, in
+ * which case the contents of <vptr> are not updated. Vectors are updated to
+ * skip the offset and the number of bytes parsed if there are enough bytes,
+ * otherwise the parsing area is left untouched. The code assumes the caller
+ * has checked that the offset is smaller than or equal to the number of bytes
+ * in the vectors.
+ */
+static inline size_t vp_get_varint_ofs(struct ist *v1, struct ist *v2, size_t ofs, uint64_t *vptr)
+{
+ size_t data = v1->len + v2->len;
+ const char *head, *wrap;
+ uint64_t v = 0;
+ int bits = 0;
+ size_t ret;
+
+ BUG_ON_HOT(ofs > data);
+
+ vp_skip(v1, v2, ofs);
+
+ /* let's see where we start from. The wrapping area only concerns the
+ * end of the first area, even if it's empty it does not overlap with
+ * the second one so we don't care about v1 being set or not.
+ */
+ head = v1->len ? v1->ptr : v2->ptr;
+ wrap = v1->ptr + v1->len;
+ data -= ofs;
+
+ if (data != 0 && ((uint8_t)*head >= 0xF0)) {
+ v = (uint8_t)*head;
+ bits += 4;
+ while (1) {
+ if (++head == wrap)
+ head = v2->ptr;
+ data--;
+ if (!data || !(*head & 0x80))
+ break;
+ v += (uint64_t)(uint8_t)*head << bits;
+ bits += 7;
+ }
+ }
+
+ /* last byte */
+ if (!data)
+ return 0;
+
+ v += (uint64_t)(uint8_t)*head << bits;
+ *vptr = v;
+ data--;
+
+ ret = v1->len + v2->len - data;
+ vp_skip(v1, v2, ret);
+ return ret;
+}
+
+/* vp_get_varint(): try to decode a varint from a pair of vectors, starting at
+ * the head, into value <vptr>. Returns the number of bytes parsed in case of
+ * success, or 0 if there were not enough bytes, in which case the contents of
+ * <vptr> are not updated. Vectors are updated to skip the bytes parsed if
+ * there are enough bytes, otherwise they're left untouched.
+ */
+static inline size_t vp_get_varint(struct ist *v1, struct ist *v2, uint64_t *vptr)
+{
+ return vp_get_varint_ofs(v1, v2, 0, vptr);
+}
+
+/* vp_peek_varint_ofs(): try to decode a varint from a pair of vectors, starting at
+ * the head, into value <vptr>. Returns the number of bytes parsed in case of
+ * success, or 0 if there were not enough bytes, in which case the contents of
+ * <vptr> are not updated.
+ */
+static inline size_t vp_peek_varint_ofs(struct ist v1, struct ist v2, size_t ofs, uint64_t *vptr)
+{
+ return vp_get_varint_ofs(&v1, &v2, ofs, vptr);
+}
+
+
+/************************************************************/
+/* ring-buffer API */
+/* This is used to manipulate rings made of (head,tail) */
+/* It creates vectors for reading (data) and writing (room) */
+/************************************************************/
+
+/* build 2 vectors <v1> and <v2> corresponding to the available data in ring
+ * buffer of size <size>, starting at address <area>, with a head <head> and
+ * a tail <tail>. <v2> is non-empty only if the data wraps (i.e. tail<head).
+ */
+static inline void vp_ring_to_data(struct ist *v1, struct ist *v2, char *area, size_t size, size_t head, size_t tail)
+{
+ v1->ptr = area + head;
+ v1->len = ((head <= tail) ? tail : size) - head;
+ v2->ptr = area;
+ v2->len = (tail < head) ? tail : 0;
+}
+
+/* build 2 vectors <v1> and <v2> corresponding to the available room in ring
+ * buffer of size <size>, starting at address <area>, with a head <head> and
+ * a tail <tail>. <v2> is non-empty only if the room wraps (i.e. head>tail).
+ */
+static inline void vp_ring_to_room(struct ist *v1, struct ist *v2, char *area, size_t size, size_t head, size_t tail)
+{
+ v1->ptr = area + tail;
+ v1->len = ((tail <= head) ? head : size) - tail;
+ v2->ptr = area;
+ v2->len = (head < tail) ? head : 0;
+}
+
+/* Set a ring's <head> and <tail> according to the data area represented by the
+ * concatenation of <v1> and <v2> which must point to two adjacent areas within
+ * a ring buffer of <size> bytes starting at <area>. <v1>, if not empty, starts
+ * at the head and <v2>, if not empty, ends at the tail. If both vectors are of
+ * length zero, the ring is considered empty and both its head and tail will be
+ * reset.
+ */
+static inline void vp_data_to_ring(const struct ist v1, const struct ist v2, char *area, size_t size, size_t *head, size_t *tail)
+{
+ size_t ofs;
+
+ if (!v1.len && !v2.len) {
+ *head = *tail = 0;
+ return;
+ }
+
+ ofs = (v1.len ? v1.ptr : v2.ptr) - area;
+ if (ofs >= size)
+ ofs -= size;
+ *head = ofs;
+
+ ofs = (v2.len ? v2.ptr + v2.len : v1.ptr + v1.len) - area;
+ if (ofs >= size)
+ ofs -= size;
+ *tail = ofs;
+}
+
+/* Set a ring's <head> and <tail> according to the room area represented by the
+ * concatenation of <v1> and <v2> which must point to two adjacent areas within
+ * a ring buffer of <size> bytes starting at <area>. <v1>, if not empty, starts
+ * at the tail and <v2>, if not empty, ends at the head. If both vectors are of
+ * length zero, the ring is considered full and both its head and tail will be
+ * reset (which cannot be distinguished from empty). The caller must make sure
+ * not to fill a ring with this API.
+ */
+static inline void vp_room_to_ring(const struct ist v1, const struct ist v2, char *area, size_t size, size_t *head, size_t *tail)
+{
+ size_t ofs;
+
+ if (!v1.len && !v2.len) {
+ *head = *tail = 0;
+ return;
+ }
+
+ ofs = (v1.len ? v1.ptr : v2.ptr) - area;
+ if (ofs >= size)
+ ofs -= size;
+ *tail = ofs;
+
+ ofs = (v2.len ? v2.ptr + v2.len : v1.ptr + v1.len) - area;
+ if (ofs >= size)
+ ofs -= size;
+ *head = ofs;
+}
+
+#endif /* _HAPROXY_VECPAIR_H */
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/include/haproxy/version.h b/include/haproxy/version.h
index 651a8de..5d9c886 100644
--- a/include/haproxy/version.h
+++ b/include/haproxy/version.h
@@ -33,13 +33,13 @@
#ifdef CONFIG_PRODUCT_BRANCH
#define PRODUCT_BRANCH CONFIG_PRODUCT_BRANCH
#else
-#define PRODUCT_BRANCH "2.9"
+#define PRODUCT_BRANCH "3.0"
#endif
#ifdef CONFIG_PRODUCT_STATUS
#define PRODUCT_STATUS CONFIG_PRODUCT_STATUS
#else
-#define PRODUCT_STATUS "Status: stable branch - will stop receiving fixes around Q1 2025."
+#define PRODUCT_STATUS "Status: long-term supported branch - will stop receiving fixes around Q2 2029."
#endif
#ifdef CONFIG_PRODUCT_URL_BUGS
diff --git a/include/haproxy/xref.h b/include/haproxy/xref.h
index 42eed58..25f9d3c 100644
--- a/include/haproxy/xref.h
+++ b/include/haproxy/xref.h
@@ -28,6 +28,7 @@
#ifndef __HAPROXY_XREF_H__
#define __HAPROXY_XREF_H__
+#include <haproxy/api.h>
#include <haproxy/xref-t.h>
/* xref is used to create relation between two elements.
diff --git a/include/import/ebtree.h b/include/import/ebtree.h
index d6e51d5..31a9cac 100644
--- a/include/import/ebtree.h
+++ b/include/import/ebtree.h
@@ -250,39 +250,84 @@
#include <import/ebtree-t.h>
#include <haproxy/api.h>
-static inline int flsnz8_generic(unsigned int x)
+/* returns clz from 7 to 0 for 0x01 to 0xFF. Returns 7 for 0 as well. */
+static inline unsigned int clz8(unsigned char c)
{
- int ret = 0;
- if (x >> 4) { x >>= 4; ret += 4; }
- return ret + ((0xFFFFAA50U >> (x << 1)) & 3) + 1;
+ unsigned int r = 4;
+
+ if (c & 0xf0) {
+ r = 0;
+ c >>= 4;
+ }
+ return r + ((0x000055afU >> (c * 2)) & 0x3);
}
-/* Note: we never need to run fls on null keys, so we can optimize the fls
- * function by removing a conditional jump.
+/* FLSNZ: find last set bit for non-zero value. "Last" here means the highest
+ * one. It returns a value from 1 to 32 for 1<<0 to 1<<31.
*/
-#if defined(__i386__) || defined(__x86_64__)
-/* this code is similar on 32 and 64 bit */
-static inline int flsnz(int x)
+
+#if (defined(__i386__) || defined(__x86_64__)) && !defined(__atom__)
+/* DO NOT USE ON ATOM! The instruction is emulated and is several times slower
+ * than doing the math by hand.
+ */
+static inline unsigned int flsnz32(unsigned int x)
{
- int r;
+ unsigned int r;
__asm__("bsrl %1,%0\n"
: "=r" (r) : "rm" (x));
- return r+1;
+ return r + 1;
+}
+#define flsnz32(x) flsnz32(x)
+
+# if defined(__x86_64__)
+static inline unsigned int flsnz64(unsigned long long x)
+{
+ unsigned long long r;
+ __asm__("bsrq %1,%0\n"
+ : "=r" (r) : "rm" (x));
+ return r + 1;
+}
+# define flsnz64(x) flsnz64(x)
+# endif
+
+#elif !defined(__atom__) && defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)))
+/* gcc >= 4.2 brings __builtin_clz() and __builtin_clzl(), usable for non-x86 */
+
+static inline unsigned int flsnz32(unsigned int x)
+{
+ return 32 - __builtin_clz(x);
+}
+# define flsnz32(x) flsnz32(x)
+
+# if defined(__SIZEOF_LONG__) && (__SIZEOF_LONG__ > 4)
+static inline unsigned int flsnz64(unsigned long x)
+{
+ return (__SIZEOF_LONG__ * 8) - __builtin_clzl(x);
}
+# define flsnz64(x) flsnz64(x)
+# endif
-static inline int flsnz8(unsigned char x)
+#endif /* end of arch-specific implementations */
+
+/*** Fallback versions below ***/
+
+#ifndef flsnz8
+# if defined(flsnz32)
+# define flsnz8(x) flsnz32((unsigned char)x)
+# else
+static inline unsigned int flsnz8(unsigned int x)
{
- int r;
- __asm__("movzbl %%al, %%eax\n"
- "bsrl %%eax,%0\n"
- : "=r" (r) : "a" (x));
- return r+1;
+ unsigned int ret = 0;
+ if (x >> 4) { x >>= 4; ret += 4; }
+ return ret + ((0xFFFFAA50U >> (x << 1)) & 3) + 1;
}
+# define flsnz8(x) flsnz8(x)
+# endif
+#endif
-#else
-// returns 1 to 32 for 1<<0 to 1<<31. Undefined for 0.
-#define flsnz(___a) ({ \
- register int ___x, ___bits = 0; \
+#ifndef flsnz32
+# define flsnz32(___a) ({ \
+ register unsigned int ___x, ___bits = 0; \
___x = (___a); \
if (___x & 0xffff0000) { ___x &= 0xffff0000; ___bits += 16;} \
if (___x & 0xff00ff00) { ___x &= 0xff00ff00; ___bits += 8;} \
@@ -291,16 +336,10 @@ static inline int flsnz8(unsigned char x)
if (___x & 0xaaaaaaaa) { ___x &= 0xaaaaaaaa; ___bits += 1;} \
___bits + 1; \
})
-
-static inline int flsnz8(unsigned int x)
-{
- return flsnz8_generic(x);
-}
-
-
#endif
-static inline int fls64(unsigned long long x)
+#ifndef flsnz64
+static inline unsigned int flsnz64(unsigned long long x)
{
unsigned int h;
unsigned int bits = 32;
@@ -310,10 +349,21 @@ static inline int fls64(unsigned long long x)
h = x;
bits = 0;
}
- return flsnz(h) + bits;
+ return flsnz32(h) + bits;
}
+# define flsnz64(x) flsnz64(x)
+#endif
+
+#ifndef flsnz_long
+# define flsnz_long(x) ((sizeof(long) > 4) ? flsnz64(x) : flsnz32(x))
+#endif
-#define fls_auto(x) ((sizeof(x) > 4) ? fls64(x) : flsnz(x))
+#ifndef flsnz
+# define flsnz(x) ((sizeof(x) > 4) ? flsnz64(x) : (sizeof(x) > 1) ? flsnz32(x) : flsnz8(x))
+#endif
+
+#define fls64(x) flsnz64(x)
+#define fls_auto(x) ((x) ? flsnz(x) : 0)
/* Linux-like "container_of". It returns a pointer to the structure of type
* <type> which has its member <name> stored at address <ptr>.
@@ -720,9 +770,9 @@ static forceinline void __eb_delete(struct eb_node *node)
* bytes. Note that parts or all of <ignore> bits may be rechecked. It is only
* passed here as a hint to speed up the check.
*/
-static forceinline int equal_bits(const unsigned char *a,
- const unsigned char *b,
- int ignore, int len)
+static forceinline size_t equal_bits(const unsigned char *a,
+ const unsigned char *b,
+ size_t ignore, size_t len)
{
for (ignore >>= 3, a += ignore, b += ignore, ignore <<= 3;
ignore < len; ) {
@@ -738,7 +788,7 @@ static forceinline int equal_bits(const unsigned char *a,
* it as the number of identical bits. Note that low bit numbers are
* assigned to high positions in the byte, as we compare them as strings.
*/
- ignore -= flsnz8(c);
+ ignore -= flsnz_long(c);
break;
}
}
@@ -786,12 +836,12 @@ static forceinline int check_bits(const unsigned char *a,
* permitted. Equal strings are reported as a negative number of bits, which
* indicates the end was reached.
*/
-static forceinline int string_equal_bits(const unsigned char *a,
- const unsigned char *b,
- int ignore)
+static forceinline size_t string_equal_bits(const unsigned char *a,
+ const unsigned char *b,
+ size_t ignore)
{
- int beg;
- unsigned char c;
+ unsigned char c, d;
+ size_t beg;
beg = ignore >> 3;
@@ -799,8 +849,6 @@ static forceinline int string_equal_bits(const unsigned char *a,
* or at the first zero we encounter on either side.
*/
while (1) {
- unsigned char d;
-
c = a[beg];
d = b[beg];
beg++;
@@ -809,14 +857,14 @@ static forceinline int string_equal_bits(const unsigned char *a,
if (c)
break;
if (!d)
- return -1;
+ return (size_t)-1;
}
/* OK now we know that a and b differ at byte <beg>, or that both are zero.
* We have to find what bit is differing and report it as the number of
* identical bits. Note that low bit numbers are assigned to high positions
* in the byte, as we compare them as strings.
*/
- return (beg << 3) - flsnz8(c);
+ return (beg << 3) - flsnz(c);
}
static forceinline int cmp_bits(const unsigned char *a, const unsigned char *b, unsigned int pos)
diff --git a/include/import/ist.h b/include/import/ist.h
index e4e1425..962d63b 100644
--- a/include/import/ist.h
+++ b/include/import/ist.h
@@ -331,6 +331,25 @@ static inline struct ist istzero(const struct ist ist, size_t size)
return ret;
}
+/* Remove trailing newline characters if present in <ist> by reducing its
+ * length. Both '\n', '\r' and '\n\r' match. Return the modified ist.
+ */
+static inline struct ist iststrip(const struct ist ist)
+{
+ struct ist ret = ist;
+
+ if (ret.len) {
+ if (ret.ptr[ret.len - 1] == '\n')
+ --ret.len;
+ }
+ if (ret.len) {
+ if (ret.ptr[ret.len - 1] == '\r')
+ --ret.len;
+ }
+
+ return ret;
+}
+
/* returns the ordinal difference between two strings :
* < 0 if ist1 < ist2
* = 0 if ist1 == ist2
diff --git a/include/import/slz-tables.h b/include/import/slz-tables.h
index 0b3a5b9..6e6d658 100644
--- a/include/import/slz-tables.h
+++ b/include/import/slz-tables.h
@@ -1,3 +1,5 @@
+#include <inttypes.h>
+
/* Fixed Huffman table as per RFC1951.
*
* Lit Value Bits Codes
diff --git a/include/import/xxhash.h b/include/import/xxhash.h
index a18e8c7..7c3c3fc 100644
--- a/include/import/xxhash.h
+++ b/include/import/xxhash.h
@@ -3387,7 +3387,7 @@ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(XXH_NOESCAPE const XXH64_can
/* === Compiler specifics === */
-#if ((defined(sun) || defined(__sun)) && __cplusplus) /* Solaris includes __STDC_VERSION__ with C++. Tested with GCC 5.5 */
+#if ((defined(sun) || defined(__sun)) && defined(__cplusplus) && __cplusplus) /* Solaris includes __STDC_VERSION__ with C++. Tested with GCC 5.5 */
# define XXH_RESTRICT /* disable */
#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* >= C99 */
# define XXH_RESTRICT restrict
diff --git a/include/make/options.mk b/include/make/options.mk
index 022981c..d212586 100644
--- a/include/make/options.mk
+++ b/include/make/options.mk
@@ -23,14 +23,15 @@ build_options = $(foreach opt,$(use_opts),$(call ignore_implicit,$(opt)))
# Make a list of all known features with +/- prepended depending on their
# activation status. Must be a macro so that dynamically enabled ones are
# evaluated with their current status.
-build_features = $(foreach opt,$(patsubst USE_%,%,$(sort $(use_opts))),$(if $(USE_$(opt)),+$(opt),-$(opt)))
+build_features = $(foreach opt,$(patsubst USE_%,%,$(sort $(use_opts))),$(if $(USE_$(opt):0=),+$(opt),-$(opt)))
-# This returns a list of -DUSE_* for all known USE_* that are set
-opts_as_defines = $(foreach opt,$(use_opts),$(if $($(opt)),-D$(opt),))
+# This returns a list of -DUSE_* for all known USE_* that are set to anything
+# neither empty nor '0'.
+opts_as_defines = $(foreach opt,$(use_opts),$(if $($(opt):0=),-D$(opt),))
# Lists all enabled or disabled options without the "USE_" prefix
-enabled_opts = $(foreach opt,$(patsubst USE_%,%,$(use_opts)),$(if $(USE_$(opt)),$(opt),))
-disabled_opts = $(foreach opt,$(patsubst USE_%,%,$(use_opts)),$(if $(USE_$(opt)),,$(opt)))
+enabled_opts = $(foreach opt,$(patsubst USE_%,%,$(use_opts)),$(if $(USE_$(opt):0=),$(opt),))
+disabled_opts = $(foreach opt,$(patsubst USE_%,%,$(use_opts)),$(if $(USE_$(opt):0=),,$(opt)))
# preset all XXX_{INC,LIB,CFLAGS,LDFLAGS,SRC} variables to empty for $1=XXX
reset_opt_vars = $(foreach name,INC LIB CFLAGS LDFLAGS SRC,$(eval $(1)_$(name)=))
@@ -50,3 +51,15 @@ endef
# collect all enabled USE_foo's foo_{C,LD}FLAGS into OPTIONS_{C,LD}FLAGS
collect_opts_flags = $(foreach opt,$(enabled_opts),$(eval $(call collect_opt_flags,$(opt))))
+
+# Check that any USE_* variable that was forced actually exist. For this we'll
+# build a list of the MAKEOVERRIDES variables that start with USE_*, and keep
+# the ones that do not match any of the patterns built by appending '=%' to all
+# use_opts. The outstanding ones are thus unknown and each of them produces a
+# warning.
+warn_unknown_options = \
+ $(foreach unknown, \
+ $(filter-out $(foreach opt,$(use_opts),$(opt:==%)), \
+ $(foreach opt,$(MAKEOVERRIDES), \
+ $(strip $(filter USE_%,$(opt))))), \
+ $(warning Warning: ignoring unknown build option: $(unknown)))
diff --git a/include/make/verbose.mk b/include/make/verbose.mk
index c37d513..6ee10a0 100644
--- a/include/make/verbose.mk
+++ b/include/make/verbose.mk
@@ -10,6 +10,7 @@ endif
# or to themselves depending on the verbosity level.
ifeq ($V,1)
cmd_CC = $(CC)
+cmd_CXX = $(CXX)
cmd_LD = $(LD)
cmd_AR = $(AR)
cmd_MAKE = +$(MAKE)
@@ -17,12 +18,14 @@ else
ifeq (3.81,$(firstword $(sort $(MAKE_VERSION) 3.81)))
# 3.81 or above
cmd_CC = $(info $ CC $@) $(Q)$(CC)
+cmd_CXX = $(info $ CXX $@) $(Q)$(CXX)
cmd_LD = $(info $ LD $@) $(Q)$(LD)
cmd_AR = $(info $ AR $@) $(Q)$(AR)
cmd_MAKE = $(info $ MAKE $@) $(Q)+$(MAKE)
else
# 3.80 or older
cmd_CC = $(Q)echo " CC $@";$(CC)
+cmd_CXX = $(Q)echo " CXX $@";$(CXX)
cmd_LD = $(Q)echo " LD $@";$(LD)
cmd_AR = $(Q)echo " AR $@";$(AR)
cmd_MAKE = $(Q)echo " MAKE $@";$(MAKE)