MEDIUM: http: add support for "set-tos" in http-request/http-response

This manipulates the TOS field of the IP header of outgoing packets sent
to the client. This can be used to set a specific DSCP traffic class based
on some request or response information. See RFC2474, 2597, 3260 and 4594
for more information.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index ef80690..c59b966 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -2669,7 +2669,7 @@
 
 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-nice <nice> | set-log-level <level> | set-tos <tos> }
              [ { if | unless } <condition> ]
   Access control for Layer 7 requests
 
@@ -2743,6 +2743,15 @@
       rule wins. This rule can be useful to disable health checks coming from
       another equipment.
 
+    - "set-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 behaviour on
+      border routers based on some information from the request. See RFC 2474,
+      2597, 3260 and 4594 for more information.
+
   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
@@ -2831,6 +2840,15 @@
       rule wins. This rule can be useful to disable health checks coming from
       another equipment.
 
+    - "set-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 behaviour on
+      border routers based on some information from the request. See RFC 2474,
+      2597, 3260 and 4594 for more information.
+
   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 b201055..89980b1 100644
--- a/include/types/proto_http.h
+++ b/include/types/proto_http.h
@@ -248,6 +248,7 @@
 	HTTP_REQ_ACT_REDIR,
 	HTTP_REQ_ACT_SET_NICE,
 	HTTP_REQ_ACT_SET_LOGL,
+	HTTP_REQ_ACT_SET_TOS,
 	HTTP_REQ_ACT_MAX /* must always be last */
 };
 
@@ -260,6 +261,7 @@
 	HTTP_RES_ACT_SET_HDR,
 	HTTP_RES_ACT_SET_NICE,
 	HTTP_RES_ACT_SET_LOGL,
+	HTTP_RES_ACT_SET_TOS,
 	HTTP_RES_ACT_MAX /* must always be last */
 };
 
@@ -374,6 +376,7 @@
 		struct redirect_rule *redir;   /* redirect rule or "http-request redirect" */
 		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 */
 	} arg;                                 /* arguments used by some actions */
 };
 
@@ -389,6 +392,7 @@
 		} hdr_add;                     /* args used by "add-header" and "set-header" */
 		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 */
 	} arg;                                 /* arguments used by some actions */
 };
 
diff --git a/src/proto_http.c b/src/proto_http.c
index fe79c15..ea9e14c 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -3211,6 +3211,13 @@
 			s->task->nice = rule->arg.nice;
 			break;
 
+		case HTTP_REQ_ACT_SET_TOS:
+#ifdef IP_TOS
+			if (s->req->prod->conn->addr.to.ss_family == AF_INET)
+				setsockopt(s->req->prod->conn->t.sock.fd, IPPROTO_IP, IP_TOS, &rule->arg.tos, sizeof(rule->arg.tos));
+#endif
+			break;
+
 		case HTTP_REQ_ACT_SET_LOGL:
 			s->logs.level = rule->arg.loglevel;
 			break;
@@ -3284,6 +3291,13 @@
 			s->task->nice = rule->arg.nice;
 			break;
 
+		case HTTP_RES_ACT_SET_TOS:
+#ifdef IP_TOS
+			if (s->req->prod->conn->addr.to.ss_family == AF_INET)
+				setsockopt(s->req->prod->conn->t.sock.fd, IPPROTO_IP, IP_TOS, &rule->arg.tos, sizeof(rule->arg.tos));
+#endif
+			break;
+
 		case HTTP_RES_ACT_SET_LOGL:
 			s->logs.level = rule->arg.loglevel;
 			break;
@@ -8420,6 +8434,30 @@
 		else if (rule->arg.nice > 1024)
 			rule->arg.nice = 1024;
 		cur_arg++;
+	} else if (!strcmp(args[0], "set-tos")) {
+#ifdef IP_TOS
+		char *err;
+		rule->action = HTTP_REQ_ACT_SET_TOS;
+		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.tos = strtol(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++;
+#else
+		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-log-level")) {
 		rule->action = HTTP_REQ_ACT_SET_LOGL;
 		cur_arg = 1;
@@ -8475,7 +8513,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-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-log-level', but got '%s'%s.\n",
 		      file, linenum, args[0], *args[0] ? "" : " (missing argument)");
 		goto out_err;
 	}
@@ -8539,6 +8577,30 @@
 		else if (rule->arg.nice > 1024)
 			rule->arg.nice = 1024;
 		cur_arg++;
+	} else if (!strcmp(args[0], "set-tos")) {
+#ifdef IP_TOS
+		char *err;
+		rule->action = HTTP_RES_ACT_SET_TOS;
+		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.tos = strtol(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++;
+#else
+		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-log-level")) {
 		rule->action = HTTP_RES_ACT_SET_LOGL;
 		cur_arg = 1;
@@ -8575,7 +8637,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-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-log-level', but got '%s'%s.\n",
 		      file, linenum, args[0], *args[0] ? "" : " (missing argument)");
 		goto out_err;
 	}