MINOR: http-act/tcp-act: Add "set-mark" and "set-tos" for tcp content rules

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.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 7843e04..9ffcc75 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -11897,6 +11897,16 @@
         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 @@
       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 @@
     - 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 @@
   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 @@
         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 @@
     - 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
diff --git a/include/haproxy/action-t.h b/include/haproxy/action-t.h
index 4f02163..773ccd1 100644
--- a/include/haproxy/action-t.h
+++ b/include/haproxy/action-t.h
@@ -81,8 +81,6 @@
 
 	/* common http actions .*/
 	ACT_HTTP_REDIR,
-	ACT_HTTP_SET_TOS,
-	ACT_HTTP_SET_MARK,
 
 	/* http request actions. */
 	ACT_HTTP_REQ_TARPIT,
diff --git a/src/http_act.c b/src/http_act.c
index 9e49d33..b77fd93 100644
--- a/src/http_act.c
+++ b/src/http_act.c
@@ -1315,71 +1315,6 @@
 	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 @@
 		{ "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 @@
 		{ "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 },
diff --git a/src/http_ana.c b/src/http_ana.c
index fc0b5bc..63ee760 100644
--- a/src/http_ana.c
+++ b/src/http_ana.c
@@ -2831,14 +2831,6 @@
 					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 @@
 				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))
diff --git a/src/tcp_act.c b/src/tcp_act.c
index a6f0596..cb6c75f 100644
--- a/src/tcp_act.c
+++ b/src/tcp_act.c
@@ -236,6 +236,22 @@
 	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)
@@ -279,7 +295,76 @@
 
 	(*orig_arg)++;
 
+	return ACT_RET_PRS_OK;
+}
+
+
+/* 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
 }
 
 
@@ -296,10 +381,12 @@
 
 
 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 @@
 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 @@
 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 @@
 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 @@
 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 */ }
 }};