MEDIUM: http: add the "set-mark" action on http-request/http-response rules

"set-mark" is used to set the Netfilter MARK on 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.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index c59b966..a04c6f7 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -2669,7 +2669,8 @@
 
 http-request { allow | deny | tarpit | auth [realm <realm>] | redirect <rule> |
               add-header <name> <fmt> | set-header <name> <fmt> |
-              set-nice <nice> | set-log-level <level> | set-tos <tos> }
+              set-nice <nice> | set-log-level <level> | set-tos <tos> |
+              set-mark <mark> }
              [ { if | unless } <condition> ]
   Access control for Layer 7 requests
 
@@ -2752,6 +2753,15 @@
       border routers based on some information from the request. See RFC 2474,
       2597, 3260 and 4594 for more information.
 
+    - "set-mark" is used to set the Netfilter MARK on 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.
+
   There is no limit to the number of http-request statements per instance.
 
   It is important to know that http-request rules are processed very early in
@@ -2788,8 +2798,8 @@
              about ACL usage.
 
 http-response { allow | deny | add-header <name> <fmt> | set-nice <nice> |
-                set-header <name> <fmt> | set-log-level <level> }
-                [ { if | unless } <condition> ]
+                set-header <name> <fmt> | set-log-level <level> |
+                set-mark <mark> } [ { if | unless } <condition> ]
   Access control for Layer 7 responses
 
   May be used in sections:   defaults | frontend | listen | backend
@@ -2849,6 +2859,15 @@
       border routers based on some information from the request. See RFC 2474,
       2597, 3260 and 4594 for more information.
 
+    - "set-mark" is used to set the Netfilter MARK on 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.
+
   There is no limit to the number of http-response statements per instance.
 
   It is important to know that http-reqsponse rules are processed very early in
diff --git a/include/types/proto_http.h b/include/types/proto_http.h
index 89980b1..1d7c92f 100644
--- a/include/types/proto_http.h
+++ b/include/types/proto_http.h
@@ -249,6 +249,7 @@
 	HTTP_REQ_ACT_SET_NICE,
 	HTTP_REQ_ACT_SET_LOGL,
 	HTTP_REQ_ACT_SET_TOS,
+	HTTP_REQ_ACT_SET_MARK,
 	HTTP_REQ_ACT_MAX /* must always be last */
 };
 
@@ -262,6 +263,7 @@
 	HTTP_RES_ACT_SET_NICE,
 	HTTP_RES_ACT_SET_LOGL,
 	HTTP_RES_ACT_SET_TOS,
+	HTTP_RES_ACT_SET_MARK,
 	HTTP_RES_ACT_MAX /* must always be last */
 };
 
@@ -377,6 +379,7 @@
 		int nice;                      /* nice value for HTTP_REQ_ACT_SET_NICE */
 		int loglevel;                  /* log-level value for HTTP_REQ_ACT_SET_LOGL */
 		int tos;                       /* tos value for HTTP_REQ_ACT_SET_TOS */
+		int mark;                      /* nfmark value for HTTP_REQ_ACT_SET_MARK */
 	} arg;                                 /* arguments used by some actions */
 };
 
@@ -393,6 +396,7 @@
 		int nice;                      /* nice value for HTTP_RES_ACT_SET_NICE */
 		int loglevel;                  /* log-level value for HTTP_RES_ACT_SET_LOGL */
 		int tos;                       /* tos value for HTTP_RES_ACT_SET_TOS */
+		int mark;                      /* nfmark value for HTTP_RES_ACT_SET_MARK */
 	} arg;                                 /* arguments used by some actions */
 };
 
diff --git a/src/proto_http.c b/src/proto_http.c
index ea9e14c..2164a85 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -3218,6 +3218,12 @@
 #endif
 			break;
 
+		case HTTP_REQ_ACT_SET_MARK:
+#ifdef SO_MARK
+			setsockopt(s->req->prod->conn->t.sock.fd, SOL_SOCKET, SO_MARK, &rule->arg.mark, sizeof(rule->arg.mark));
+#endif
+			break;
+
 		case HTTP_REQ_ACT_SET_LOGL:
 			s->logs.level = rule->arg.loglevel;
 			break;
@@ -3298,6 +3304,12 @@
 #endif
 			break;
 
+		case HTTP_RES_ACT_SET_MARK:
+#ifdef SO_MARK
+			setsockopt(s->req->prod->conn->t.sock.fd, SOL_SOCKET, SO_MARK, &rule->arg.mark, sizeof(rule->arg.mark));
+#endif
+			break;
+
 		case HTTP_RES_ACT_SET_LOGL:
 			s->logs.level = rule->arg.loglevel;
 			break;
@@ -8458,6 +8470,31 @@
 		Alert("parsing [%s:%d]: 'http-request %s' is not supported on this platform (IP_TOS undefined).\n", file, linenum, args[0]);
 		goto out_err;
 #endif
+	} else if (!strcmp(args[0], "set-mark")) {
+#ifdef SO_MARK
+		char *err;
+		rule->action = HTTP_REQ_ACT_SET_MARK;
+		cur_arg = 1;
+
+		if (!*args[cur_arg] ||
+		    (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
+			Alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument (integer/hex value).\n",
+			      file, linenum, args[0]);
+			goto out_err;
+		}
+
+		rule->arg.mark = strtoul(args[cur_arg], &err, 0);
+		if (err && *err != '\0') {
+			Alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-request %s' (integer/hex value expected).\n",
+			      file, linenum, err, args[0]);
+			goto out_err;
+		}
+		cur_arg++;
+		global.last_checks |= LSTCHK_NETADM;
+#else
+		Alert("parsing [%s:%d]: 'http-request %s' is not supported on this platform (SO_MARK undefined).\n", file, linenum, args[0]);
+		goto out_err;
+#endif
 	} else if (!strcmp(args[0], "set-log-level")) {
 		rule->action = HTTP_REQ_ACT_SET_LOGL;
 		cur_arg = 1;
@@ -8513,7 +8550,7 @@
 		cur_arg = 2;
 		return rule;
 	} else {
-		Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', 'tarpit', 'add-header', 'set-header', 'set-nice', 'set-tos', 'set-log-level', but got '%s'%s.\n",
+		Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', 'tarpit', 'add-header', 'set-header', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', but got '%s'%s.\n",
 		      file, linenum, args[0], *args[0] ? "" : " (missing argument)");
 		goto out_err;
 	}
@@ -8601,6 +8638,31 @@
 		Alert("parsing [%s:%d]: 'http-response %s' is not supported on this platform (IP_TOS undefined).\n", file, linenum, args[0]);
 		goto out_err;
 #endif
+	} else if (!strcmp(args[0], "set-mark")) {
+#ifdef SO_MARK
+		char *err;
+		rule->action = HTTP_RES_ACT_SET_MARK;
+		cur_arg = 1;
+
+		if (!*args[cur_arg] ||
+		    (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
+			Alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer/hex value).\n",
+			      file, linenum, args[0]);
+			goto out_err;
+		}
+
+		rule->arg.mark = strtoul(args[cur_arg], &err, 0);
+		if (err && *err != '\0') {
+			Alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-response %s' (integer/hex value expected).\n",
+			      file, linenum, err, args[0]);
+			goto out_err;
+		}
+		cur_arg++;
+		global.last_checks |= LSTCHK_NETADM;
+#else
+		Alert("parsing [%s:%d]: 'http-response %s' is not supported on this platform (SO_MARK undefined).\n", file, linenum, args[0]);
+		goto out_err;
+#endif
 	} else if (!strcmp(args[0], "set-log-level")) {
 		rule->action = HTTP_RES_ACT_SET_LOGL;
 		cur_arg = 1;
@@ -8637,7 +8699,7 @@
 				       (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR);
 		cur_arg += 2;
 	} else {
-		Alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', 'add-header', 'set-header', 'set-nice', 'set-tos', 'set-log-level', but got '%s'%s.\n",
+		Alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', 'add-header', 'set-header', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', but got '%s'%s.\n",
 		      file, linenum, args[0], *args[0] ? "" : " (missing argument)");
 		goto out_err;
 	}