From 87ea407cce6a9993609d808fe0edd1466cae924a Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Mon, 22 Dec 2025 11:59:33 +0100 Subject: [PATCH] MINOR: proxy: refactor proxy inheritance of a defaults section If a proxy is referencing a defaults instance, some checks must be performed to ensure that inheritance will be compatible. Refcount of the defaults instance may also be incremented if some settings cannot be copied. This operation is performed when parsing a new proxy of defaults section which references a defaults, either implicitely or explicitely. This patch extracts this code into a dedicated function named proxy_ref_defaults(). This in turn may call defaults_px_ref() (previously called proxy_ref_defaults()) to increment its refcount. The objective of this patch is to be able to reuse defaults inheritance validation for dynamic backends created at runtime, outside of the parsing code. --- include/haproxy/proxy.h | 3 +- src/cfgparse-listen.c | 82 ++------------------------------ src/proxy.c | 102 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 105 insertions(+), 82 deletions(-) diff --git a/include/haproxy/proxy.h b/include/haproxy/proxy.h index afa6297c3..089ab9e22 100644 --- a/include/haproxy/proxy.h +++ b/include/haproxy/proxy.h @@ -74,8 +74,7 @@ void defaults_px_destroy_all_unref(void); void defaults_px_detach(struct proxy *px); void defaults_px_ref_all(void); void defaults_px_unref_all(void); - -void proxy_ref_defaults(struct proxy *px, struct proxy *defpx); +int proxy_ref_defaults(struct proxy *px, struct proxy *defpx, char **errmsg); void proxy_unref_defaults(struct proxy *px); int setup_new_proxy(struct proxy *px, const char *name, unsigned int cap, char **errmsg); struct proxy *alloc_new_proxy(const char *name, unsigned int cap, diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c index bcb085936..68b2eba88 100644 --- a/src/cfgparse-listen.c +++ b/src/cfgparse-listen.c @@ -501,84 +501,10 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) curproxy->conf.file_prev = file_prev; curproxy->conf.line_prev = line_prev; - if (curr_defproxy && (!LIST_ISEMPTY(&curr_defproxy->http_req_rules) || - !LIST_ISEMPTY(&curr_defproxy->http_res_rules) || - !LIST_ISEMPTY(&curr_defproxy->http_after_res_rules) || - !LIST_ISEMPTY(&curr_defproxy->tcp_req.l4_rules) || - !LIST_ISEMPTY(&curr_defproxy->tcp_req.l5_rules) || - !LIST_ISEMPTY(&curr_defproxy->tcp_req.inspect_rules) || - !LIST_ISEMPTY(&curr_defproxy->tcp_rep.inspect_rules))) { - /* If the current default proxy defines TCP/HTTP rules, the - * current proxy will keep a reference on it. But some sanity - * checks are performed first: - * - * - It cannot be used to init a defaults section - * - It cannot be used to init a listen section - * - It cannot be used to init backend and frontend sections at - * same time. It can be used to init several sections of the - * same type only. - * - It cannot define L4/L5 TCP rules if it is used to init - * backend sections. - * - It cannot define 'tcp-response content' rules if it - * is used to init frontend sections. - * - * If no error is found, refcount of the default proxy is incremented. - */ - - /* Note: Add tcpcheck_rules too if unresolve args become allowed in defaults section */ - if (rc & PR_CAP_DEF) { - ha_alert("parsing [%s:%d]: a defaults section cannot inherit from a defaults section defining TCP/HTTP rules (defaults section at %s:%d).\n", - file, linenum, curr_defproxy->conf.file, curr_defproxy->conf.line); - err_code |= ERR_ALERT | ERR_ABORT; - } - else if ((rc & PR_CAP_LISTEN) == PR_CAP_LISTEN) { - ha_alert("parsing [%s:%d]: a listen section cannot inherit from a defaults section defining TCP/HTTP rules.\n", - file, linenum); - err_code |= ERR_ALERT | ERR_ABORT; - } - else { - char defcap = (curr_defproxy->cap & PR_CAP_LISTEN); - - if ((defcap == PR_CAP_BE || defcap == PR_CAP_FE) && (rc & PR_CAP_LISTEN) != defcap) { - ha_alert("parsing [%s:%d]: frontends and backends cannot inherit from the same defaults section" - " if it defines TCP/HTTP rules (defaults section at %s:%d).\n", - file, linenum, curr_defproxy->conf.file, curr_defproxy->conf.line); - err_code |= ERR_ALERT | ERR_ABORT; - } - else if (!(rc & PR_CAP_FE) && (!LIST_ISEMPTY(&curr_defproxy->tcp_req.l4_rules) || - !LIST_ISEMPTY(&curr_defproxy->tcp_req.l5_rules))) { - ha_alert("parsing [%s:%d]: a backend section cannot inherit from a defaults section defining" - " 'tcp-request connection' or 'tcp-request session' rules (defaults section at %s:%d).\n", - file, linenum, curr_defproxy->conf.file, curr_defproxy->conf.line); - err_code |= ERR_ALERT | ERR_ABORT; - } - else if (!(rc & PR_CAP_BE) && !LIST_ISEMPTY(&curr_defproxy->tcp_rep.inspect_rules)) { - ha_alert("parsing [%s:%d]: a frontend section cannot inherit from a defaults section defining" - " 'tcp-response content' rules (defaults section at %s:%d).\n", - file, linenum, curr_defproxy->conf.file, curr_defproxy->conf.line); - err_code |= ERR_ALERT | ERR_ABORT; - } - else { - curr_defproxy->cap = (curr_defproxy->cap & ~PR_CAP_LISTEN) | (rc & PR_CAP_LISTEN); - proxy_ref_defaults(curproxy, curr_defproxy); - } - } - } - - if (curr_defproxy && (curr_defproxy->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) && - (curproxy->cap & PR_CAP_LISTEN) == PR_CAP_BE) { - /* If the current default proxy defines tcpcheck rules, the - * current proxy will keep a reference on it. but only if the - * current proxy has the backend capability. - */ - proxy_ref_defaults(curproxy, curr_defproxy); - } - - if ((rc & PR_CAP_BE) && curr_defproxy && (curr_defproxy->nb_req_cap || curr_defproxy->nb_rsp_cap)) { - ha_alert("parsing [%s:%d]: backend or defaults sections cannot inherit from a defaults section defining" - " capptures (defaults section at %s:%d).\n", - file, linenum, curr_defproxy->conf.file, curr_defproxy->conf.line); - err_code |= ERR_ALERT | ERR_ABORT; + if (curr_defproxy) { + err_code = proxy_ref_defaults(curproxy, curr_defproxy, &errmsg); + if (err_code) + ha_alert("parsing [%s:%d]: %s.\n", file, linenum, errmsg); } if (rc & PR_CAP_DEF) { diff --git a/src/proxy.c b/src/proxy.c index 7f573659e..fc65f3c06 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -3010,15 +3010,113 @@ void defaults_px_unref_all(void) * refcount is incremented by one. For now, this operation is not thread safe * and is perform during init stage only. */ -void proxy_ref_defaults(struct proxy *px, struct proxy *defpx) +static inline void defaults_px_ref(struct proxy *defpx, struct proxy *px) { if (px->defpx == defpx) return; - BUG_ON(px->defpx != NULL); + /* is already referencing another defaults. */ + BUG_ON(px->defpx); + px->defpx = defpx; defpx->conf.refcount++; } +/* Check that can inherits from default proxy. If some settings + * cannot be copied, refcount of the defaults instance is incremented. + * Inheritance may be impossible due to incompatibility issues. In this case, + * will be allocated to point to a textual description of the error. + * + * Returns ERR_NONE on success and a combination of ERR_CODE on failure + */ +int proxy_ref_defaults(struct proxy *px, struct proxy *defpx, char **errmsg) +{ + char defcap = defpx->cap & PR_CAP_LISTEN; + int err_code = ERR_NONE; + + if ((px->cap & PR_CAP_BE) && (defpx->nb_req_cap || defpx->nb_rsp_cap)) { + memprintf(errmsg, "backend or defaults sections cannot inherit from a defaults section defining" + " captures (defaults section at %s:%d)", + defpx->conf.file, defpx->conf.line); + err_code |= ERR_ALERT | ERR_ABORT; + goto out; + } + + /* If the current default proxy defines TCP/HTTP rules, the + * current proxy will keep a reference on it. But some sanity + * checks are performed first: + * + * - It cannot be used to init a defaults section + * - It cannot be used to init a listen section + * - It cannot be used to init backend and frontend sections at + * same time. It can be used to init several sections of the + * same type only. + * - It cannot define L4/L5 TCP rules if it is used to init + * backend sections. + * - It cannot define 'tcp-response content' rules if it + * is used to init frontend sections. + * + * If no error is found, refcount of the default proxy is incremented. + */ + if ((!LIST_ISEMPTY(&defpx->http_req_rules) || + !LIST_ISEMPTY(&defpx->http_res_rules) || + !LIST_ISEMPTY(&defpx->http_after_res_rules) || + !LIST_ISEMPTY(&defpx->tcp_req.l4_rules) || + !LIST_ISEMPTY(&defpx->tcp_req.l5_rules) || + !LIST_ISEMPTY(&defpx->tcp_req.inspect_rules) || + !LIST_ISEMPTY(&defpx->tcp_rep.inspect_rules))) { + + /* Note: Add tcpcheck_rules too if unresolve args become allowed in defaults section */ + if (px->cap & PR_CAP_DEF) { + memprintf(errmsg, "a defaults section cannot inherit from a defaults section defining TCP/HTTP rules (defaults section at %s:%d)", + defpx->conf.file, defpx->conf.line); + err_code |= ERR_ALERT | ERR_ABORT; + goto out; + } + else if ((px->cap & PR_CAP_LISTEN) == PR_CAP_LISTEN) { + memprintf(errmsg, "a listen section cannot inherit from a defaults section defining TCP/HTTP rules"); + err_code |= ERR_ALERT | ERR_ABORT; + goto out; + } + else if ((defcap == PR_CAP_BE || defcap == PR_CAP_FE) && (px->cap & PR_CAP_LISTEN) != defcap) { + memprintf(errmsg, "frontends and backends cannot inherit from the same defaults section" + " if it defines TCP/HTTP rules (defaults section at %s:%d)", + defpx->conf.file, defpx->conf.line); + err_code |= ERR_ALERT | ERR_ABORT; + goto out; + } + else if (!(px->cap & PR_CAP_FE) && (!LIST_ISEMPTY(&defpx->tcp_req.l4_rules) || + !LIST_ISEMPTY(&defpx->tcp_req.l5_rules))) { + memprintf(errmsg, "a backend section cannot inherit from a defaults section defining" + " 'tcp-request connection' or 'tcp-request session' rules (defaults section at %s:%d)", + defpx->conf.file, defpx->conf.line); + err_code |= ERR_ALERT | ERR_ABORT; + goto out; + } + else if (!(px->cap & PR_CAP_BE) && !LIST_ISEMPTY(&defpx->tcp_rep.inspect_rules)) { + memprintf(errmsg, "a frontend section cannot inherit from a defaults section defining" + " 'tcp-response content' rules (defaults section at %s:%d)", + defpx->conf.file, defpx->conf.line); + err_code |= ERR_ALERT | ERR_ABORT; + goto out; + } + + defpx->cap = (defpx->cap & ~PR_CAP_LISTEN) | (px->cap & PR_CAP_LISTEN); + defaults_px_ref(defpx, px); + } + + if ((defpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) && + (px->cap & PR_CAP_LISTEN) == PR_CAP_BE) { + /* If the current default proxy defines tcpcheck rules, the + * current proxy will keep a reference on it. but only if the + * current proxy has the backend capability. + */ + defaults_px_ref(defpx, px); + } + + out: + return err_code; +} + /* proxy removes its reference on its default proxy. The default proxy * refcount is decremented by one. If it was the last reference, the * corresponding default proxy is destroyed. For now this operation is not -- 2.47.3