MEDIUM: tcp: add new tcp action "silent-drop"

This stops the evaluation of the rules and makes the client-facing
connection suddenly disappear using a system-dependant way that tries
to prevent the client from being notified. The effect it then that the
client still sees an established connection while there's none on
HAProxy. The purpose is to achieve a comparable effect to "tarpit"
except that it doesn't use any local resource at all on the machine
running HAProxy. It can resist much higher loads than "tarpit", and
slow down stronger attackers. It is important to undestand the impact
of using this mechanism. All stateful equipments placed between the
client and HAProxy (firewalls, proxies, load balancers) will also keep
the established connection for a long time and may suffer from this
action. On modern Linux systems running with enough privileges, the
TCP_REPAIR socket option is used to block the emission of a TCP
reset. On other systems, the socket's TTL is reduced to 1 so that the
TCP reset doesn't pass the first router, though it's still delivered to
local networks.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index f21add2..1aa7cdd 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -3409,6 +3409,7 @@
               { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] |
               sc-inc-gpc0(<sc-id>) |
               sc-set-gpt0(<sc-id>) <int> |
+              silent-drop |
               lua <function name>
              }
              [ { if | unless } <condition> ]
@@ -3441,7 +3442,8 @@
       efficient against very dumb robots, and will significantly reduce the
       load on firewalls compared to a "deny" rule. But when facing "correctly"
       developed robots, it can make things worse by forcing haproxy and the
-      front firewall to support insane number of concurrent connections.
+      front firewall to support insane number of concurrent connections. See
+      also the "silent-drop" action below.
 
     - "auth" : this stops the evaluation of the rules and immediately responds
       with an HTTP 401 or 407 error code to invite the user to present a valid
@@ -3727,6 +3729,23 @@
 
       When set-src is successful, the source port is set to 0.
 
+    - "silent-drop" : this stops the evaluation of the rules and makes the
+      client-facing connection suddenly disappear using a system-dependant way
+      that tries to prevent the client from being notified. The effect it then
+      that the client still sees an established connection while there's none
+      on HAProxy. The purpose is to achieve a comparable effect to "tarpit"
+      except that it doesn't use any local resource at all on the machine
+      running HAProxy. It can resist much higher loads than "tarpit", and slow
+      down stronger attackers. It is important to undestand the impact of using
+      this mechanism. All stateful equipments placed between the client and
+      HAProxy (firewalls, proxies, load balancers) will also keep the
+      established connection for a long time and may suffer from this action.
+      On modern Linux systems running with enough privileges, the TCP_REPAIR
+      socket option is used to block the emission of a TCP reset. On other
+      systems, the socket's TTL is reduced to 1 so that the TCP reset doesn't
+      pass the first router, though it's still delivered to local networks. Do
+      not use it unless you fully understand how it works.
+
   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
@@ -3796,6 +3815,7 @@
                 set-var(<var-name>) <expr> |
                 sc-inc-gpc0(<sc-id>) |
                 sc-set-gpt0(<sc-id>) <int> |
+                silent-drop |
                 lua <function name>
               }
               [ { if | unless } <condition> ]
@@ -4014,6 +4034,23 @@
       designated by <sc-id>. If an error occurs, this action silently fails and
       the actions evaluation continues.
 
+    - "silent-drop" : this stops the evaluation of the rules and makes the
+      client-facing connection suddenly disappear using a system-dependant way
+      that tries to prevent the client from being notified. The effect it then
+      that the client still sees an established connection while there's none
+      on HAProxy. The purpose is to achieve a comparable effect to "tarpit"
+      except that it doesn't use any local resource at all on the machine
+      running HAProxy. It can resist much higher loads than "tarpit", and slow
+      down stronger attackers. It is important to undestand the impact of using
+      this mechanism. All stateful equipments placed between the client and
+      HAProxy (firewalls, proxies, load balancers) will also keep the
+      established connection for a long time and may suffer from this action.
+      On modern Linux systems running with enough privileges, the TCP_REPAIR
+      socket option is used to block the emission of a TCP reset. On other
+      systems, the socket's TTL is reduced to 1 so that the TCP reset doesn't
+      pass the first router, though it's still delivered to local networks. Do
+      not use it unless you fully understand how it works.
+
   There is no limit to the number of http-response statements per instance.
 
   It is important to know that http-response rules are processed very early in
@@ -8516,6 +8553,24 @@
         an error occurs, this action silently fails and the actions evaluation
         continues.
 
+    - "silent-drop" :
+        This stops the evaluation of the rules and makes the client-facing
+        connection suddenly disappear using a system-dependant way that tries
+        to prevent the client from being notified. The effect it then that the
+        client still sees an established connection while there's none on
+        HAProxy. The purpose is to achieve a comparable effect to "tarpit"
+        except that it doesn't use any local resource at all on the machine
+        running HAProxy. It can resist much higher loads than "tarpit", and
+        slow down stronger attackers. It is important to undestand the impact
+        of using this mechanism. All stateful equipments placed between the
+        client and HAProxy (firewalls, proxies, load balancers) will also keep
+        the established connection for a long time and may suffer from this
+        action.  On modern Linux systems running with enough privileges, the
+        TCP_REPAIR socket option is used to block the emission of a TCP
+        reset. On other systems, the socket's TTL is reduced to 1 so that the
+        TCP reset doesn't pass the first router, though it's still delivered to
+        local networks. Do not use it unless you fully understand how it works.
+
   Note that the "if/unless" condition is optional. If no condition is set on
   the action, it is simply performed unconditionally. That can be useful for
   "track-sc*" actions as well as for changing the default action to a reject.
@@ -8588,6 +8643,7 @@
     - set-gpt0(<sc-id>) <int>
     - lua <function>
     - set-var(<var-name>) <expr>
+    - silent-drop
 
   They have the same meaning as their counter-parts in "tcp-request connection"
   so please refer to that section for a complete description.
@@ -8808,6 +8864,24 @@
         an error occurs, this action silently fails and the actions evaluation
         continues.
 
+    - "silent-drop" :
+        This stops the evaluation of the rules and makes the client-facing
+        connection suddenly disappear using a system-dependant way that tries
+        to prevent the client from being notified. The effect it then that the
+        client still sees an established connection while there's none on
+        HAProxy. The purpose is to achieve a comparable effect to "tarpit"
+        except that it doesn't use any local resource at all on the machine
+        running HAProxy. It can resist much higher loads than "tarpit", and
+        slow down stronger attackers. It is important to undestand the impact
+        of using this mechanism. All stateful equipments placed between the
+        client and HAProxy (firewalls, proxies, load balancers) will also keep
+        the established connection for a long time and may suffer from this
+        action.  On modern Linux systems running with enough privileges, the
+        TCP_REPAIR socket option is used to block the emission of a TCP
+        reset. On other systems, the socket's TTL is reduced to 1 so that the
+        TCP reset doesn't pass the first router, though it's still delivered to
+        local networks. Do not use it unless you fully understand how it works.
+
   Note that the "if/unless" condition is optional. If no condition is set on
   the action, it is simply performed unconditionally. That can be useful for
   for changing the default action to a reject.
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 9c0dc37..f698889 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -25,6 +25,8 @@
 #include <sys/un.h>
 
 #include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
 
 #include <common/cfgparse.h>
 #include <common/compat.h>
@@ -38,6 +40,7 @@
 #include <types/global.h>
 #include <types/capture.h>
 #include <types/server.h>
+#include <types/connection.h>
 
 #include <proto/acl.h>
 #include <proto/action.h>
@@ -49,6 +52,7 @@
 #include <proto/log.h>
 #include <proto/port_range.h>
 #include <proto/protocol.h>
+#include <proto/proto_http.h>
 #include <proto/proto_tcp.h>
 #include <proto/proxy.h>
 #include <proto/sample.h>
@@ -1416,6 +1420,65 @@
 	return result;
 }
 
+/* Executes the "silent-drop" action. May be called from {tcp,http}{request,response} */
+static enum act_return tcp_exec_action_silent_drop(struct act_rule *rule, struct proxy *px, struct session *sess, struct stream *strm, int flags)
+{
+	struct connection *conn = objt_conn(sess->origin);
+
+	if (!conn)
+		goto out;
+
+	if (!conn_ctrl_ready(conn))
+		goto out;
+
+	conn_sock_drain(conn);
+#ifdef TCP_QUICKACK
+	/* re-enable quickack if it was disabled to ack all data and avoid
+	 * retransmits from the client that might trigger a real reset.
+	 */
+	setsockopt(conn->t.sock.fd, SOL_TCP, TCP_QUICKACK, &one, sizeof(one));
+#endif
+	/* lingering must absolutely be disabled so that we don't send a
+	 * shutdown(), this is critical to the TCP_REPAIR trick. When no stream
+	 * is present, returning with ERR will cause lingering to be disabled.
+	 */
+	if (strm)
+		strm->si[0].flags |= SI_FL_NOLINGER;
+
+#ifdef TCP_REPAIR
+	if (setsockopt(conn->t.sock.fd, SOL_TCP, TCP_REPAIR, &one, sizeof(one)) == 0) {
+		/* socket will be quiet now */
+		goto out;
+	}
+#endif
+	/* either TCP_REPAIR is not defined or it failed (eg: permissions).
+	 * Let's fall back on the TTL trick, though it only works for routed
+	 * network and has no effect on local net.
+	 */
+#ifdef IP_TTL
+	setsockopt(conn->t.sock.fd, SOL_IP, IP_TTL, &one, sizeof(one));
+#endif
+ out:
+	/* kill the stream if any */
+	if (strm) {
+		channel_abort(&strm->req);
+		channel_abort(&strm->res);
+		strm->req.analysers = 0;
+		strm->res.analysers = 0;
+		strm->be->be_counters.denied_req++;
+		if (!(strm->flags & SF_ERR_MASK))
+			strm->flags |= SF_ERR_PRXCOND;
+		if (!(strm->flags & SF_FINST_MASK))
+			strm->flags |= SF_FINST_R;
+	}
+
+	sess->fe->fe_counters.denied_req++;
+	if (sess->listener->counters)
+		sess->listener->counters->denied_req++;
+
+	return ACT_RET_STOP;
+}
+
 /* Parse a tcp-response rule. Return a negative value in case of failure */
 static int tcp_parse_response_rule(char **args, int arg, int section_type,
                                    struct proxy *curpx, struct proxy *defpx,
@@ -1953,6 +2016,17 @@
 	return -1;
 }
 
+/* Parse a "silent-drop" action. It takes no argument. It returns ACT_RET_PRS_OK on
+ * success, ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret tcp_parse_silent_drop(const char **args, int *orig_arg, struct proxy *px,
+                                                struct act_rule *rule, char **err)
+{
+	rule->action     = ACT_CUSTOM;
+	rule->action_ptr = tcp_exec_action_silent_drop;
+	return ACT_RET_PRS_OK;
+}
+
 
 /************************************************************************/
 /*       All supported sample fetch functions must be declared here     */
@@ -2299,6 +2373,33 @@
 	{ NULL, NULL, 0 },
 }};
 
+
+static struct action_kw_list tcp_req_conn_actions = {ILH, {
+	{ "silent-drop", tcp_parse_silent_drop },
+	{ /* END */ }
+}};
+
+static struct action_kw_list tcp_req_cont_actions = {ILH, {
+	{ "silent-drop", tcp_parse_silent_drop },
+	{ /* END */ }
+}};
+
+static struct action_kw_list tcp_res_cont_actions = {ILH, {
+	{ "silent-drop", tcp_parse_silent_drop },
+	{ /* END */ }
+}};
+
+static struct action_kw_list http_req_actions = {ILH, {
+	{ "silent-drop", tcp_parse_silent_drop },
+	{ /* END */ }
+}};
+
+static struct action_kw_list http_res_actions = {ILH, {
+	{ "silent-drop", tcp_parse_silent_drop },
+	{ /* END */ }
+}};
+
+
 __attribute__((constructor))
 static void __tcp_protocol_init(void)
 {
@@ -2308,6 +2409,11 @@
 	cfg_register_keywords(&cfg_kws);
 	acl_register_keywords(&acl_kws);
 	bind_register_keywords(&bind_kws);
+	tcp_req_conn_keywords_register(&tcp_req_conn_actions);
+	tcp_req_cont_keywords_register(&tcp_req_cont_actions);
+	tcp_res_cont_keywords_register(&tcp_res_cont_actions);
+	http_req_keywords_register(&http_req_actions);
+	http_res_keywords_register(&http_res_actions);
 }