]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: proxy: refactor proxy inheritance of a defaults section
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 22 Dec 2025 10:59:33 +0000 (11:59 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 6 Feb 2026 13:35:18 +0000 (14:35 +0100)
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
src/cfgparse-listen.c
src/proxy.c

index afa6297c3bc6cfa8b3bca1c06cd8a7f21566a7fc..089ab9e222dfeebca76f9f26ca683559fe0ba44e 100644 (file)
@@ -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,
index bcb085936bf36089994beb978a7bf6021ccc44f6..68b2eba88ba5654d1cda3b278d522a3929a084ed 100644 (file)
@@ -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) {
index 7f573659e1795a8b49409c930813bf795634671c..fc65f3c0668f80d08b71d7d0e7de78e46ddc3f0f 100644 (file)
@@ -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);
+       /* <px> is already referencing another defaults. */
+       BUG_ON(px->defpx);
+
        px->defpx = defpx;
        defpx->conf.refcount++;
 }
 
+/* Check that <px> can inherits from <defpx> 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,
+ * <errmsg> 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 <px> 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