From cff6d757e3ba609c08ef2aaa00f07e53551e5bf6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 3 Jun 2024 07:11:10 +0200 Subject: Adding upstream version 3.0.0. Signed-off-by: Daniel Baumann --- include/haproxy/action-t.h | 13 +- include/haproxy/applet-t.h | 32 ++- include/haproxy/applet.h | 249 +++++++++++++--- include/haproxy/atomic.h | 20 +- include/haproxy/backend-t.h | 15 +- include/haproxy/backend.h | 10 +- include/haproxy/buf.h | 204 ++++++++++++- include/haproxy/bug.h | 109 ++++--- include/haproxy/cbuf-t.h | 1 + include/haproxy/cfgparse.h | 1 + include/haproxy/channel.h | 76 ++++- include/haproxy/cli-t.h | 28 +- include/haproxy/compat.h | 12 +- include/haproxy/connection-t.h | 83 +++--- include/haproxy/connection.h | 34 +-- include/haproxy/counters-t.h | 15 +- include/haproxy/defaults.h | 48 +++- include/haproxy/dgram-t.h | 2 + include/haproxy/dns-t.h | 14 +- include/haproxy/dns_ring-t.h | 110 +++++++ include/haproxy/dns_ring.h | 46 +++ include/haproxy/dynbuf-t.h | 73 +++++ include/haproxy/dynbuf.h | 150 +++++++++- include/haproxy/fcgi-app-t.h | 5 +- include/haproxy/filters-t.h | 1 + include/haproxy/freq_ctr.h | 8 + include/haproxy/global-t.h | 10 +- include/haproxy/global.h | 1 + include/haproxy/guid-t.h | 15 + include/haproxy/guid.h | 16 ++ include/haproxy/h1.h | 1 - include/haproxy/h3.h | 40 ++- include/haproxy/http.h | 17 ++ include/haproxy/http_ana-t.h | 4 +- include/haproxy/http_client-t.h | 1 + include/haproxy/http_client.h | 1 + include/haproxy/http_htx-t.h | 9 +- include/haproxy/htx-t.h | 17 +- include/haproxy/intops.h | 126 ++++++++ include/haproxy/jwt-t.h | 1 + include/haproxy/lb_ss-t.h | 32 +++ include/haproxy/lb_ss.h | 33 +++ include/haproxy/linuxcap.h | 1 + include/haproxy/list.h | 7 + include/haproxy/listener-t.h | 9 +- include/haproxy/listener.h | 7 + include/haproxy/log-t.h | 120 ++++---- include/haproxy/log.h | 51 ++-- include/haproxy/mqtt-t.h | 1 + include/haproxy/mux_h1-t.h | 23 +- include/haproxy/mux_quic-t.h | 46 ++- include/haproxy/mux_quic.h | 13 +- include/haproxy/net_helper.h | 28 ++ include/haproxy/openssl-compat.h | 28 ++ include/haproxy/pattern-t.h | 8 +- include/haproxy/peers-t.h | 102 ++++++- include/haproxy/peers.h | 22 +- include/haproxy/pool.h | 4 +- include/haproxy/proto_quic.h | 4 + include/haproxy/proto_rhttp.h | 1 + include/haproxy/proto_sockpair.h | 2 + include/haproxy/proto_udp.h | 2 + include/haproxy/protobuf.h | 10 +- include/haproxy/protocol-t.h | 11 + include/haproxy/proxy-t.h | 42 +-- include/haproxy/proxy.h | 14 +- include/haproxy/qmux_http.h | 1 - include/haproxy/qpack-dec.h | 17 +- include/haproxy/qpack-t.h | 7 + include/haproxy/qpack-tbl-t.h | 2 + include/haproxy/queue-t.h | 1 + include/haproxy/quic_ack-t.h | 4 + include/haproxy/quic_ack.h | 6 + include/haproxy/quic_cc-t.h | 6 +- include/haproxy/quic_cc_hystart.h | 129 +++++++++ include/haproxy/quic_conn-t.h | 11 +- include/haproxy/quic_conn.h | 2 +- include/haproxy/quic_fctl-t.h | 15 + include/haproxy/quic_fctl.h | 19 ++ include/haproxy/quic_rx-t.h | 7 + include/haproxy/quic_rx.h | 2 - include/haproxy/quic_sock-t.h | 4 +- include/haproxy/quic_sock.h | 8 + include/haproxy/quic_ssl.h | 4 - include/haproxy/quic_tls-t.h | 11 +- include/haproxy/quic_tls.h | 10 +- include/haproxy/quic_tx-t.h | 3 + include/haproxy/quic_tx.h | 10 +- include/haproxy/receiver-t.h | 1 + include/haproxy/resolvers-t.h | 5 +- include/haproxy/resolvers.h | 2 +- include/haproxy/ring-t.h | 59 +++- include/haproxy/ring.h | 81 +++++- include/haproxy/sample.h | 1 + include/haproxy/sc_strm.h | 35 ++- include/haproxy/server-t.h | 73 ++++- include/haproxy/server.h | 33 +-- include/haproxy/session-t.h | 17 +- include/haproxy/session.h | 79 +++-- include/haproxy/shctx.h | 2 +- include/haproxy/sink-t.h | 3 +- include/haproxy/sink.h | 16 +- include/haproxy/sock.h | 35 ++- include/haproxy/ssl_ckch-t.h | 39 +++ include/haproxy/ssl_ckch.h | 21 +- include/haproxy/ssl_crtlist.h | 2 +- include/haproxy/ssl_gencert.h | 35 +++ include/haproxy/ssl_ocsp-t.h | 6 +- include/haproxy/ssl_ocsp.h | 3 +- include/haproxy/ssl_sock-t.h | 12 +- include/haproxy/ssl_sock.h | 14 +- include/haproxy/stats-file-t.h | 12 + include/haproxy/stats-file.h | 25 ++ include/haproxy/stats-html-t.h | 21 ++ include/haproxy/stats-html.h | 22 ++ include/haproxy/stats-json.h | 24 ++ include/haproxy/stats-proxy.h | 14 + include/haproxy/stats-t.h | 506 ++++++++++++++++---------------- include/haproxy/stats.h | 69 +++-- include/haproxy/stconn-t.h | 38 ++- include/haproxy/stconn.h | 115 ++++---- include/haproxy/stick_table-t.h | 16 +- include/haproxy/stick_table.h | 63 +++- include/haproxy/stream-t.h | 13 +- include/haproxy/stream.h | 2 +- include/haproxy/systemd.h | 7 + include/haproxy/task-t.h | 18 ++ include/haproxy/tcpcheck-t.h | 26 +- include/haproxy/tcpcheck.h | 4 + include/haproxy/thread-t.h | 55 ++++ include/haproxy/thread.h | 56 ---- include/haproxy/tinfo-t.h | 22 +- include/haproxy/tools-t.h | 18 ++ include/haproxy/tools.h | 57 +++- include/haproxy/vars.h | 5 + include/haproxy/vecpair.h | 588 ++++++++++++++++++++++++++++++++++++++ include/haproxy/version.h | 4 +- include/haproxy/xref.h | 1 + include/import/ebtree.h | 136 ++++++--- include/import/ist.h | 19 ++ include/import/slz-tables.h | 2 + include/import/xxhash.h | 2 +- include/make/options.mk | 23 +- include/make/verbose.mk | 3 + 144 files changed, 4035 insertions(+), 1043 deletions(-) create mode 100644 include/haproxy/dns_ring-t.h create mode 100644 include/haproxy/dns_ring.h create mode 100644 include/haproxy/guid-t.h create mode 100644 include/haproxy/guid.h create mode 100644 include/haproxy/lb_ss-t.h create mode 100644 include/haproxy/lb_ss.h create mode 100644 include/haproxy/quic_cc_hystart.h create mode 100644 include/haproxy/quic_fctl-t.h create mode 100644 include/haproxy/quic_fctl.h create mode 100644 include/haproxy/ssl_gencert.h create mode 100644 include/haproxy/stats-file-t.h create mode 100644 include/haproxy/stats-file.h create mode 100644 include/haproxy/stats-html-t.h create mode 100644 include/haproxy/stats-html.h create mode 100644 include/haproxy/stats-json.h create mode 100644 include/haproxy/stats-proxy.h create mode 100644 include/haproxy/systemd.h create mode 100644 include/haproxy/vecpair.h (limited to 'include') 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 #include #include +#include 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; @@ -191,6 +192,10 @@ struct act_rule { struct server *srv; /* target server to attach the connection */ 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 */ 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 #include #include +#include #include /* 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 #include #include +#include #include #include @@ -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 #include +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 after the buffer's area, and for exactly 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 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 after the buffer's head, and limited to no more than * bytes. The caller is responsible for ensuring that neither @@ -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 set, and delimited by one of these characters, + * and returns the initial part and the first of such delimiters. A single + * escape character in may be specified so that when not 0 and found, + * the character that follows it is never taken as a delimiter. Note that + * 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. 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. 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 from into + * the buffer, starting from absolute 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 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 bytes from block to the end of * buffer 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 #include #include @@ -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 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 #include 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 is greater + * than 0, the 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 #include #include +#include +#include #include /* 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__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 from to the current thread. If + * 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 #include +#include #include #include #include @@ -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 + /* 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 + /* 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 +#include #include /* 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 #include #include +#include #include -#include #include #include #include @@ -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 +#include +#include + +/* 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 +#include +#include + +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 + +/* 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 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 #include #include +#include #include 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 + * (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 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 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 , and returns + * non-zero if the criticality allows to queue a request, otherwise returns + * zero. If the 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 with the given + * and , and returns non-zero if the criticality allows to queue a request, + * otherwise returns zero. If the 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 from its list at for thread 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 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 #include #include +#include #include #include #include @@ -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 +#include /* 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 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 +#include + +/* 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 + +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 #include #include +#include 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 for the presence of status code . + * 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 #include 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 #include +#include #include /* 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 is below the + * value or above +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 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 and not lower than -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 is above the + * value or below -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 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 and not greater than +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 is outside of + * the range defined by to 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 is below the + * value or above +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 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 and not lower than -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 is above the + * value or below -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 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 and not greater than +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 is outside of + * the range defined by to 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 #include #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 +#include + +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 +#include +#include + +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 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 #include +#include #include #include #include @@ -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 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 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); @@ -130,27 +146,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) */ @@ -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 #include /* 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 #include #include +#include #include #include #include @@ -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 connection app context. */ int (*init)(struct qcc *qcc); + /* Finish connection initialization if prelude required. */ + int (*finalize)(void *ctx); + + /* Initialize 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 stream closure. */ int (*close)(struct qcs *qcs, enum qcc_app_ops_close_side side); + /* Free 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 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 into destination . The * first segment is composed of bytes at p1. The remaining byte(s), if any, * are read from . may be zero and may also be larger than . 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 #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 #include +/* 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 +#include +#include + 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 + 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 + 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 #include #include -#include +#include #include #include #include @@ -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 + 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 + /* * 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 #include +#include 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 +#include +#include + /* 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 + +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 { /* 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 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 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 + +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 + +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 +#include +#include +#include +#include + +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 . */ - struct mt_list handler_list; /* elem to quic_dghdlr . */ + struct list recv_list; /* element pointing to quic_receiver_buf . */ + struct mt_list handler_list; /* element pointing to quic_dghdlr . */ }; /* 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 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 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 +#include #include #include #include @@ -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 +#include + 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 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 /* 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 #include #include +#include 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 over ring for no more than bytes or no + * more than the amount of data present in . It's assumed that the + * destination ring is always large enough for . 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 #include #include -#include +#include #include #include #include @@ -41,6 +41,7 @@ #include #include #include +#include #include @@ -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 #include +#include #include #include #include +#include #include #include #include @@ -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); @@ -117,14 +126,6 @@ void server_recalc_eweight(struct server *sv, int must_update); 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 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 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 to the server list of the session . 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 to the private conns list of session . 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 #include -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 +#include + +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 + +#include + +#include +#include + +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 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 + +#include +#include +#include +#include +#include + +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 +#include +#include + +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 + +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 #include +#include /* 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 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 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 #include #include +#include #include 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 #include #include +#include #include +#include 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 of len present in table , 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 to the number of cumulated front glitches in the tracked counter + * . 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 + /* 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 + */ + 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 #include #include +#include #include #include #include @@ -399,11 +400,11 @@ int addr_is_local(const struct netns_entry *ns, * with the hexadecimal representation of their ASCII-code (2 digits) * prefixed by , and will store the result between (included) * and (excluded), and will always terminate the string with a '\0' - * before . The position of the '\0' is returned if the conversion - * completes. If bytes are missing between and , then the - * conversion will be incomplete and truncated. If <= , the '\0' - * cannot even be stored so we return without writing the 0. + * before . If bytes are missing between and , 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 (included) and (excluded). This * function will always try to terminate the resulting string with a '\0' - * before , and will return its position if the conversion - * completes. + * before . + * + * 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 port is forbidden as client source using . */ +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 #include #include +#include #include 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 +#include +#include +#include + + +/* 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 + * 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 + * 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

within either + * v1 or v2, or a size equal to the sum of both lengths if

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

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 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 after the head, and for up to bytes. The + * caller is responsible for ensuring that 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 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 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 after the head, and for up to bytes. + * The caller is responsible for ensuring that 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 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 after the head, and for exactly bytes. + * The caller is responsible for ensuring that does not exceed the total + * number of bytes available in the areas. The function will check that it is + * indeed possible to put bytes after 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 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 after the head, and for exactly bytes. + * The caller is responsible for ensuring that neither nor + + * 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 bytes. The caller is + * responsible for ensuring that 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 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 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 after the head, into value . Returns the number of + * bytes parsed in case of success, or 0 if there were not enough bytes, in + * which case the contents of 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 . Returns the number of bytes parsed in case of + * success, or 0 if there were not enough bytes, in which case the contents of + * 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 . Returns the number of bytes parsed in case of + * success, or 0 if there were not enough bytes, in which case the contents of + * 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 and corresponding to the available data in ring + * buffer of size , starting at address , with a head and + * a tail . is non-empty only if the data wraps (i.e. tailptr = area + head; + v1->len = ((head <= tail) ? tail : size) - head; + v2->ptr = area; + v2->len = (tail < head) ? tail : 0; +} + +/* build 2 vectors and corresponding to the available room in ring + * buffer of size , starting at address , with a head and + * a tail . 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 and according to the data area represented by the + * concatenation of and which must point to two adjacent areas within + * a ring buffer of bytes starting at . , if not empty, starts + * at the head and , 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 and according to the room area represented by the + * concatenation of and which must point to two adjacent areas within + * a ring buffer of bytes starting at . , if not empty, starts + * at the tail and , 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 #include /* 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 #include -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 * which has its member stored at address . @@ -720,9 +770,9 @@ static forceinline void __eb_delete(struct eb_node *node) * bytes. Note that parts or all of 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 , 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 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 + /* 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) -- cgit v1.2.3