]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: http-act/tcp-act: Add "set-mark" and "set-tos" for tcp content rules
authorChristopher Faulet <cfaulet@haproxy.com>
Fri, 25 Jun 2021 13:11:35 +0000 (15:11 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 25 Jun 2021 14:11:58 +0000 (16:11 +0200)
It is now possible to set the Netfilter MARK and the TOS field value in all
packets sent to the client from any tcp-request rulesets or the "tcp-response
content" one. To do so, the parsing of "set-mark" and "set-tos" actions are
moved in tcp_act.c and the actions evaluation is handled in dedicated functions.

This patch may be backported as far as 2.2 if necessary.

doc/configuration.txt
include/haproxy/action-t.h
src/http_act.c
src/http_ana.c
src/tcp_act.c

index 7843e04401d62bb0f52640a308170b5d94ab71f2..9ffcc7581c562a9b6fc4c5608c699daea0348256 100644 (file)
@@ -11897,6 +11897,16 @@ tcp-request connection <action> [{if | unless} <condition>]
         expected result is a boolean. If an error occurs, this action silently
         fails and the actions evaluation continues.
 
+    - set-mark <mark>:
+      Is used to set the Netfilter MARK in all packets sent to the client to
+      the value passed in <mark> on platforms which support it. This value is
+      an unsigned 32 bit value which can be matched by netfilter and by the
+      routing table. It can be expressed both in decimal or hexadecimal format
+      (prefixed by "0x"). This can be useful to force certain packets to take a
+      different route (for example a cheaper network path for bulk
+      downloads). This works on Linux kernels 2.6.32 and above and requires
+      admin privileges.
+
     - set-src <expr> :
       Is used to set the source IP address to the value of specified
       expression. Useful if you want to mask source IP for privacy.
@@ -11963,6 +11973,17 @@ tcp-request connection <action> [{if | unless} <condition>]
       long as the address family supports a port, otherwise it forces the
       destination address to IPv4 "0.0.0.0" before rewriting the port.
 
+    - set-tos <tos>:
+      Is used to set the TOS or DSCP field value of packets sent to the client
+      to the value passed in <tos> on platforms which support this. This value
+      represents the whole 8 bits of the IP TOS field, and can be expressed
+      both in decimal or hexadecimal format (prefixed by "0x"). Note that only
+      the 6 higher bits are used in DSCP or TOS, and the two lower bits are
+      always 0. This can be used to adjust some routing behavior on border
+      routers based on some information from the request.
+
+      See RFC 2474, 2597, 3260 and 4594 for more information.
+
     - "silent-drop" :
         This stops the evaluation of the rules and makes the client-facing
         connection suddenly disappear using a system-dependent way that tries
@@ -12057,9 +12078,11 @@ tcp-request content <action> [{if | unless} <condition>]
     - set-dst <expr>
     - set-dst-port <expr>
     - set-log-level <level>
+    - set-mark <mark>
     - set-nice <nice>
     - set-src <expr>
     - set-src-port <expr>
+    - set-tos <tos>
     - set-var(<var-name>) <expr>
     - switch-mode http [ proto <name> ]
     - unset-var(<var-name>)
@@ -12113,12 +12136,18 @@ tcp-request content <action> [{if | unless} <condition>]
   The "set-log-level" is used to set the log level of the current session. More
   information on how to use it at "http-request set-log-level".
 
+  The "set-mark" is used to set the Netfilter MARK in all packets sent to the
+  client. More information on how to use it at "http-request set-mark".
+
   The "set-nice" is used to set the "nice" factor of the current session. More
   information on how to use it at "http-request set-nice".
 
   The "set-src" and "set-src-port" are used to set respectively the source IP
   and port. More information on how to use it at "http-request set-src".
 
+  The "set-tos" is used to set the TOS or DSCP field value of packets sent to
+  the client. More information on how to use it at "http-request set-tos".
+
   The "set-var" is used to set the content of a variable. The variable is
   declared inline. For "tcp-request session" rules, only session-level
   variables can be used, without any layer7 contents.
@@ -12363,11 +12392,21 @@ tcp-response content <action> [{if | unless} <condition>]
         session. More information on how to use it at "http-response
         set-log-level".
 
+    - set-mark <mark>
+        The "set-mark" is used to set the Netfilter MARK in all packets sent to
+        the client. More information on how to use it at "http-response
+        set-mark".
+
     - set-nice <nice>
         The "set-nice" is used to set the "nice" factor of the current
         session. More information on how to use it at "http-response
         set-nice".
 
+    - set-tos <tos>
+        The "set-tos" is used to set the TOS or DSCP field value of packets
+        sent to the client. More information on how to use it at "http-response
+        set-tos".
+
     - set-var(<var-name>) <expr>
         Sets a variable.
 
@@ -12510,10 +12549,12 @@ tcp-request session <action> [{if | unless} <condition>]
     - sc-inc-gpc0(<sc-id>)
     - sc-inc-gpc1(<sc-id>)
     - sc-set-gpt0(<sc-id>) { <int> | <expr> }
+    - set-mark <mark>
     - set-dst <expr>
     - set-dst-port <expr>
     - set-src <expr>
     - set-src-port <expr>
+    - set-tos <tos>
     - set-var(<var-name>) <expr>
     - unset-var(<var-name>)
     - silent-drop
index 4f0216372d9c43d2538b46633051236fa96540fd..773ccd1a2a69fd746d64b7d3e598ddcc78894d0e 100644 (file)
@@ -81,8 +81,6 @@ enum act_name {
 
        /* common http actions .*/
        ACT_HTTP_REDIR,
-       ACT_HTTP_SET_TOS,
-       ACT_HTTP_SET_MARK,
 
        /* http request actions. */
        ACT_HTTP_REQ_TARPIT,
index 9e49d3373961d30f5c51278e3293f06fdddeeeb0..b77fd9361dae7ae34d72ed30e0cff8b2d25a67ca 100644 (file)
@@ -1315,71 +1315,6 @@ static enum act_parse_ret parse_http_auth(const char **args, int *orig_arg, stru
        return ACT_RET_PRS_OK;
 }
 
-/* Parse a "set-tos" action. It takes the TOS value as argument. It returns
- * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
- */
-static enum act_parse_ret parse_http_set_tos(const char **args, int *orig_arg, struct proxy *px,
-                                             struct act_rule *rule, char **err)
-{
-#ifdef IP_TOS
-       char *endp;
-       int cur_arg;
-
-       rule->action = ACT_HTTP_SET_TOS;
-
-       cur_arg = *orig_arg;
-       if (!*args[cur_arg]) {
-               memprintf(err, "expects exactly 1 argument (integer/hex value)");
-               return ACT_RET_PRS_ERR;
-       }
-       rule->arg.http.i = strtol(args[cur_arg], &endp, 0);
-       if (endp && *endp != '\0') {
-               memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
-               return ACT_RET_PRS_ERR;
-       }
-
-       LIST_INIT(&rule->arg.http.fmt);
-       *orig_arg = cur_arg + 1;
-       return ACT_RET_PRS_OK;
-#else
-       memprintf(err, "not supported on this platform (IP_TOS undefined)");
-       return ACT_RET_PRS_ERR;
-#endif
-}
-
-/* Parse a "set-mark" action. It takes the MARK value as argument. It returns
- * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
- */
-static enum act_parse_ret parse_http_set_mark(const char **args, int *orig_arg, struct proxy *px,
-                                             struct act_rule *rule, char **err)
-{
-#ifdef SO_MARK
-       char *endp;
-       int cur_arg;
-
-       rule->action = ACT_HTTP_SET_MARK;
-
-       cur_arg = *orig_arg;
-       if (!*args[cur_arg]) {
-               memprintf(err, "expects exactly 1 argument (integer/hex value)");
-               return ACT_RET_PRS_ERR;
-       }
-       rule->arg.http.i = strtoul(args[cur_arg], &endp, 0);
-       if (endp && *endp != '\0') {
-               memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
-               return ACT_RET_PRS_ERR;
-       }
-
-       LIST_INIT(&rule->arg.http.fmt);
-       *orig_arg = cur_arg + 1;
-       global.last_checks |= LSTCHK_NETADM;
-       return ACT_RET_PRS_OK;
-#else
-       memprintf(err, "not supported on this platform (SO_MARK undefined)");
-       return ACT_RET_PRS_ERR;
-#endif
-}
-
 /* This function executes a early-hint action. It adds an HTTP Early Hint HTTP
  * 103 response header with <.arg.http.str> name and with a value built
  * according to <.arg.http.fmt> log line format. If it is the first early-hint
@@ -2458,11 +2393,9 @@ static struct action_kw_list http_req_actions = {
                { "set-header",       parse_http_set_header,           0 },
                { "set-map",          parse_http_set_map,              KWF_MATCH_PREFIX },
                { "set-method",       parse_set_req_line,              0 },
-               { "set-mark",         parse_http_set_mark,             0 },
                { "set-path",         parse_set_req_line,              0 },
                { "set-pathq",        parse_set_req_line,              0 },
                { "set-query",        parse_set_req_line,              0 },
-               { "set-tos",          parse_http_set_tos,              0 },
                { "set-uri",          parse_set_req_line,              0 },
                { "strict-mode",      parse_http_strict_mode,          0 },
                { "tarpit",           parse_http_deny,                 0 },
@@ -2491,9 +2424,7 @@ static struct action_kw_list http_res_actions = {
                { "return",          parse_http_return,         0 },
                { "set-header",      parse_http_set_header,     0 },
                { "set-map",         parse_http_set_map,        KWF_MATCH_PREFIX },
-               { "set-mark",        parse_http_set_mark,       0 },
                { "set-status",      parse_http_set_status,     0 },
-               { "set-tos",         parse_http_set_tos,        0 },
                { "strict-mode",     parse_http_strict_mode,    0 },
                { "track-sc",        parse_http_track_sc,       KWF_MATCH_PREFIX },
                { "wait-for-body",   parse_http_wait_for_body,  0 },
index fc0b5bccc24c854d771824ce75aa8de7e41689d3..63ee76034992638cee5afd7ae268fed7ab1d0e25 100644 (file)
@@ -2831,14 +2831,6 @@ static enum rule_result http_req_get_intercept_rule(struct proxy *px, struct lis
                                        rule_ret = HTTP_RULE_RES_ERROR;
                                goto end;
 
-                       case ACT_HTTP_SET_TOS:
-                               conn_set_tos(objt_conn(sess->origin), rule->arg.http.i);
-                               break;
-
-                       case ACT_HTTP_SET_MARK:
-                               conn_set_mark(objt_conn(sess->origin), rule->arg.http.i);
-                               break;
-
                        /* other flags exists, but normally, they never be matched. */
                        default:
                                break;
@@ -2958,14 +2950,6 @@ resume_execution:
                                rule_ret = HTTP_RULE_RES_DENY;
                                goto end;
 
-                       case ACT_HTTP_SET_TOS:
-                               conn_set_tos(objt_conn(sess->origin), rule->arg.http.i);
-                               break;
-
-                       case ACT_HTTP_SET_MARK:
-                               conn_set_mark(objt_conn(sess->origin), rule->arg.http.i);
-                               break;
-
                        case ACT_HTTP_REDIR:
                                rule_ret = HTTP_RULE_RES_ABRT;
                                if (!http_apply_redirect_rule(rule->arg.redir, s, txn))
index a6f0596740dea05cb02d84b99db1e1371cbf4852..cb6c75f4031d99fc1ccb6ed370ecd747493326aa 100644 (file)
@@ -236,6 +236,22 @@ static enum act_return tcp_exec_action_silent_drop(struct act_rule *rule, struct
        return ACT_RET_ABRT;
 }
 
+
+static enum act_return tcp_action_set_mark(struct act_rule *rule, struct proxy *px,
+                                          struct session *sess, struct stream *s, int flags)
+{
+       conn_set_mark(objt_conn(sess->origin), (uintptr_t)rule->arg.act.p[0]);
+       return ACT_RET_CONT;
+}
+
+static enum act_return tcp_action_set_tos(struct act_rule *rule, struct proxy *px,
+                                         struct session *sess, struct stream *s, int flags)
+{
+       conn_set_tos(objt_conn(sess->origin), (uintptr_t)rule->arg.act.p[0]);
+       return ACT_RET_CONT;
+}
+
+
 /* parse "set-{src,dst}[-port]" action */
 static enum act_parse_ret tcp_parse_set_src_dst(const char **args, int *orig_arg, struct proxy *px,
                                                 struct act_rule *rule, char **err)
@@ -283,6 +299,75 @@ static enum act_parse_ret tcp_parse_set_src_dst(const char **args, int *orig_arg
 }
 
 
+/* Parse a "set-mark" action. It takes the MARK value as argument. It returns
+ * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret tcp_parse_set_mark(const char **args, int *cur_arg, struct proxy *px,
+                                            struct act_rule *rule, char **err)
+{
+#ifdef SO_MARK
+       char *endp;
+       unsigned int mark;
+
+       if (!*args[*cur_arg]) {
+               memprintf(err, "expects exactly 1 argument (integer/hex value)");
+               return ACT_RET_PRS_ERR;
+       }
+       mark = strtoul(args[*cur_arg], &endp, 0);
+       if (endp && *endp != '\0') {
+               memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
+               return ACT_RET_PRS_ERR;
+       }
+
+       (*cur_arg)++;
+
+       /* Register processing function. */
+       rule->action_ptr = tcp_action_set_mark;
+       rule->action = ACT_CUSTOM;
+       rule->arg.act.p[0] = (void *)(uintptr_t)mark;
+       global.last_checks |= LSTCHK_NETADM;
+       return ACT_RET_PRS_OK;
+#else
+       memprintf(err, "not supported on this platform (SO_MARK undefined)");
+       return ACT_RET_PRS_ERR;
+#endif
+}
+
+
+/* Parse a "set-tos" action. It takes the TOS value as argument. It returns
+ * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret tcp_parse_set_tos(const char **args, int *cur_arg, struct proxy *px,
+                                            struct act_rule *rule, char **err)
+{
+#ifdef IP_TOS
+       char *endp;
+       int tos;
+
+       if (!*args[*cur_arg]) {
+               memprintf(err, "expects exactly 1 argument (integer/hex value)");
+               return ACT_RET_PRS_ERR;
+       }
+       tos = strtol(args[*cur_arg], &endp, 0);
+       if (endp && *endp != '\0') {
+               memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
+               return ACT_RET_PRS_ERR;
+       }
+
+       (*cur_arg)++;
+
+       /* Register processing function. */
+       rule->action_ptr = tcp_action_set_tos;
+       rule->action = ACT_CUSTOM;
+       rule->arg.act.p[0] = (void *)(uintptr_t)tos;
+       return ACT_RET_PRS_OK;
+#else
+       memprintf(err, "not supported on this platform (IP_TOS undefined)");
+       return ACT_RET_PRS_ERR;
+#endif
+}
+
+
 /* Parse a "silent-drop" action. It takes no argument. It returns ACT_RET_PRS_OK on
  * success, ACT_RET_PRS_ERR on error.
  */
@@ -296,10 +381,12 @@ static enum act_parse_ret tcp_parse_silent_drop(const char **args, int *orig_arg
 
 
 static struct action_kw_list tcp_req_conn_actions = {ILH, {
+       { "set-mark",     tcp_parse_set_mark    },
        { "set-src",      tcp_parse_set_src_dst },
        { "set-src-port", tcp_parse_set_src_dst },
        { "set-dst"     , tcp_parse_set_src_dst },
        { "set-dst-port", tcp_parse_set_src_dst },
+       { "set-tos",      tcp_parse_set_tos     },
        { "silent-drop",  tcp_parse_silent_drop },
        { /* END */ }
 }};
@@ -307,10 +394,12 @@ static struct action_kw_list tcp_req_conn_actions = {ILH, {
 INITCALL1(STG_REGISTER, tcp_req_conn_keywords_register, &tcp_req_conn_actions);
 
 static struct action_kw_list tcp_req_sess_actions = {ILH, {
+       { "set-mark",     tcp_parse_set_mark    },
        { "set-src",      tcp_parse_set_src_dst },
        { "set-src-port", tcp_parse_set_src_dst },
        { "set-dst"     , tcp_parse_set_src_dst },
        { "set-dst-port", tcp_parse_set_src_dst },
+       { "set-tos",      tcp_parse_set_tos     },
        { "silent-drop",  tcp_parse_silent_drop },
        { /* END */ }
 }};
@@ -318,10 +407,12 @@ static struct action_kw_list tcp_req_sess_actions = {ILH, {
 INITCALL1(STG_REGISTER, tcp_req_sess_keywords_register, &tcp_req_sess_actions);
 
 static struct action_kw_list tcp_req_cont_actions = {ILH, {
+       { "set-mark",     tcp_parse_set_mark    },
        { "set-src",      tcp_parse_set_src_dst },
        { "set-src-port", tcp_parse_set_src_dst },
        { "set-dst"     , tcp_parse_set_src_dst },
        { "set-dst-port", tcp_parse_set_src_dst },
+       { "set-tos",      tcp_parse_set_tos     },
        { "silent-drop",  tcp_parse_silent_drop },
        { /* END */ }
 }};
@@ -329,6 +420,8 @@ static struct action_kw_list tcp_req_cont_actions = {ILH, {
 INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_cont_actions);
 
 static struct action_kw_list tcp_res_cont_actions = {ILH, {
+       { "set-mark",     tcp_parse_set_mark   },
+       { "set-tos",      tcp_parse_set_tos     },
        { "silent-drop", tcp_parse_silent_drop },
        { /* END */ }
 }};
@@ -336,17 +429,21 @@ static struct action_kw_list tcp_res_cont_actions = {ILH, {
 INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_cont_actions);
 
 static struct action_kw_list http_req_actions = {ILH, {
+       { "set-mark",     tcp_parse_set_mark    },
        { "silent-drop",  tcp_parse_silent_drop },
        { "set-src",      tcp_parse_set_src_dst },
        { "set-src-port", tcp_parse_set_src_dst },
        { "set-dst",      tcp_parse_set_src_dst },
        { "set-dst-port", tcp_parse_set_src_dst },
+       { "set-tos",      tcp_parse_set_tos     },
        { /* END */ }
 }};
 
 INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions);
 
 static struct action_kw_list http_res_actions = {ILH, {
+       { "set-mark",    tcp_parse_set_mark    },
+       { "set-tos",     tcp_parse_set_tos    },
        { "silent-drop", tcp_parse_silent_drop },
        { /* END */ }
 }};