REORG: tcp: move TCP actions from proto_tcp.c to tcp_act.c
authorWilly Tarreau <w@1wt.eu>
Fri, 28 Aug 2020 09:03:28 +0000 (11:03 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 28 Aug 2020 16:51:36 +0000 (18:51 +0200)
The file proto_tcp.c has become a real mess because it still contains
tons of definitions that have nothing to do with the TCP protocol setup.
This commit moves the ruleset actions "set-src-port", "set-dst-port",
"set-src", "set-dst", and "silent-drop" to a new file "tcp_act.c".
Nothing has changed beyond this.

Makefile
src/proto_tcp.c
src/tcp_act.c [new file with mode: 0644]

index d4c96a2..be916a9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -809,7 +809,7 @@ OBJS = src/mux_fcgi.o src/mux_h1.o src/mux_h2.o src/backend.o                 \
        src/task.o src/ring.o src/vars.o src/trace.o src/mux_pt.o              \
        src/xxhash.o src/mworker-prog.o src/h1_htx.o src/frontend.o            \
        src/extcheck.o src/channel.o src/action.o src/mailers.o                \
-       src/proto_sockpair.o src/ebmbtree.o src/thread.o                       \
+       src/tcp_act.o src/proto_sockpair.o src/ebmbtree.o src/thread.o         \
        src/lb_fwrr.o src/time.o src/regex.o src/lb_fwlc.o                     \
        src/htx.o src/h2.o src/hpack-tbl.o src/lru.o src/wdt.o                 \
        src/lb_map.o src/eb32sctree.o src/ebistree.o src/h1.o                  \
index 98a6310..b2733d6 100644 (file)
@@ -30,7 +30,6 @@
 #include <netinet/tcp.h>
 #include <netinet/in.h>
 
-#include <haproxy/action-t.h>
 #include <haproxy/api.h>
 #include <haproxy/arg.h>
 #include <haproxy/channel.h>
@@ -1207,254 +1206,6 @@ int tcp_pause_listener(struct listener *l)
        return 1;
 }
 
-/*
- * Execute the "set-src" action. May be called from {tcp,http}request.
- * It only changes the address and tries to preserve the original port. If the
- * previous family was neither AF_INET nor AF_INET6, the port is set to zero.
- */
-enum act_return tcp_action_req_set_src(struct act_rule *rule, struct proxy *px,
-                                              struct session *sess, struct stream *s, int flags)
-{
-       struct connection *cli_conn;
-
-       if ((cli_conn = objt_conn(sess->origin)) && conn_get_src(cli_conn)) {
-               struct sample *smp;
-
-               smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_ADDR);
-               if (smp) {
-                       int port = get_net_port(cli_conn->src);
-
-                       if (smp->data.type == SMP_T_IPV4) {
-                               ((struct sockaddr_in *)cli_conn->src)->sin_family = AF_INET;
-                               ((struct sockaddr_in *)cli_conn->src)->sin_addr.s_addr = smp->data.u.ipv4.s_addr;
-                               ((struct sockaddr_in *)cli_conn->src)->sin_port = port;
-                       } else if (smp->data.type == SMP_T_IPV6) {
-                               ((struct sockaddr_in6 *)cli_conn->src)->sin6_family = AF_INET6;
-                               memcpy(&((struct sockaddr_in6 *)cli_conn->src)->sin6_addr, &smp->data.u.ipv6, sizeof(struct in6_addr));
-                               ((struct sockaddr_in6 *)cli_conn->src)->sin6_port = port;
-                       }
-               }
-               cli_conn->flags |= CO_FL_ADDR_FROM_SET;
-       }
-       return ACT_RET_CONT;
-}
-
-/*
- * Execute the "set-dst" action. May be called from {tcp,http}request.
- * It only changes the address and tries to preserve the original port. If the
- * previous family was neither AF_INET nor AF_INET6, the port is set to zero.
- */
-enum act_return tcp_action_req_set_dst(struct act_rule *rule, struct proxy *px,
-                                              struct session *sess, struct stream *s, int flags)
-{
-       struct connection *cli_conn;
-
-       if ((cli_conn = objt_conn(sess->origin)) && conn_get_dst(cli_conn)) {
-               struct sample *smp;
-
-               smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_ADDR);
-               if (smp) {
-                       int port = get_net_port(cli_conn->dst);
-
-                       if (smp->data.type == SMP_T_IPV4) {
-                               ((struct sockaddr_in *)cli_conn->dst)->sin_family = AF_INET;
-                               ((struct sockaddr_in *)cli_conn->dst)->sin_addr.s_addr = smp->data.u.ipv4.s_addr;
-                       } else if (smp->data.type == SMP_T_IPV6) {
-                               ((struct sockaddr_in6 *)cli_conn->dst)->sin6_family = AF_INET6;
-                               memcpy(&((struct sockaddr_in6 *)cli_conn->dst)->sin6_addr, &smp->data.u.ipv6, sizeof(struct in6_addr));
-                               ((struct sockaddr_in6 *)cli_conn->dst)->sin6_port = port;
-                       }
-                       cli_conn->flags |= CO_FL_ADDR_TO_SET;
-               }
-       }
-       return ACT_RET_CONT;
-}
-
-/*
- * Execute the "set-src-port" action. May be called from {tcp,http}request.
- * We must test the sin_family before setting the port. If the address family
- * is neither AF_INET nor AF_INET6, the address is forced to AF_INET "0.0.0.0"
- * and the port is assigned.
- */
-enum act_return tcp_action_req_set_src_port(struct act_rule *rule, struct proxy *px,
-                                              struct session *sess, struct stream *s, int flags)
-{
-       struct connection *cli_conn;
-
-       if ((cli_conn = objt_conn(sess->origin)) && conn_get_src(cli_conn)) {
-               struct sample *smp;
-
-               smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_SINT);
-               if (smp) {
-                       if (cli_conn->src->ss_family == AF_INET6) {
-                               ((struct sockaddr_in6 *)cli_conn->src)->sin6_port = htons(smp->data.u.sint);
-                       } else {
-                               if (cli_conn->src->ss_family != AF_INET) {
-                                       cli_conn->src->ss_family = AF_INET;
-                                       ((struct sockaddr_in *)cli_conn->src)->sin_addr.s_addr = 0;
-                               }
-                               ((struct sockaddr_in *)cli_conn->src)->sin_port = htons(smp->data.u.sint);
-                       }
-               }
-       }
-       return ACT_RET_CONT;
-}
-
-/*
- * Execute the "set-dst-port" action. May be called from {tcp,http}request.
- * We must test the sin_family before setting the port. If the address family
- * is neither AF_INET nor AF_INET6, the address is forced to AF_INET "0.0.0.0"
- * and the port is assigned.
- */
-enum act_return tcp_action_req_set_dst_port(struct act_rule *rule, struct proxy *px,
-                                              struct session *sess, struct stream *s, int flags)
-{
-       struct connection *cli_conn;
-
-       if ((cli_conn = objt_conn(sess->origin)) && conn_get_dst(cli_conn)) {
-               struct sample *smp;
-
-               smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_SINT);
-               if (smp) {
-                       if (cli_conn->dst->ss_family == AF_INET6) {
-                               ((struct sockaddr_in6 *)cli_conn->dst)->sin6_port = htons(smp->data.u.sint);
-                       } else {
-                               if (cli_conn->dst->ss_family != AF_INET) {
-                                       cli_conn->dst->ss_family = AF_INET;
-                                       ((struct sockaddr_in *)cli_conn->dst)->sin_addr.s_addr = 0;
-                               }
-                               ((struct sockaddr_in *)cli_conn->dst)->sin_port = htons(smp->data.u.sint);
-                       }
-               }
-       }
-       return ACT_RET_CONT;
-}
-
-/* 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;
-
-#ifdef TCP_QUICKACK
-       /* drain is needed only to send the quick ACK */
-       conn_sock_drain(conn);
-
-       /* 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->handle.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;
-
-       /* We're on the client-facing side, we must force to disable lingering to
-        * ensure we will use an RST exclusively and kill any pending data.
-        */
-       fdtab[conn->handle.fd].linger_risk = 1;
-
-#ifdef TCP_REPAIR
-       if (setsockopt(conn->handle.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->handle.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 &= AN_REQ_FLT_END;
-               strm->res.analysers &= AN_RES_FLT_END;
-               if (strm->flags & SF_BE_ASSIGNED)
-                       _HA_ATOMIC_ADD(&strm->be->be_counters.denied_req, 1);
-               if (!(strm->flags & SF_ERR_MASK))
-                       strm->flags |= SF_ERR_PRXCOND;
-               if (!(strm->flags & SF_FINST_MASK))
-                       strm->flags |= SF_FINST_R;
-       }
-
-       _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
-       if (sess->listener->counters)
-               _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
-
-       return ACT_RET_ABRT;
-}
-
-/* parse "set-{src,dst}[-port]" action */
-enum act_parse_ret tcp_parse_set_src_dst(const char **args, int *orig_arg, struct proxy *px, struct act_rule *rule, char **err)
-{
-       int cur_arg;
-       struct sample_expr *expr;
-       unsigned int where;
-
-       cur_arg = *orig_arg;
-       expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line, err, &px->conf.args, NULL);
-       if (!expr)
-               return ACT_RET_PRS_ERR;
-
-       where = 0;
-       if (px->cap & PR_CAP_FE)
-               where |= SMP_VAL_FE_HRQ_HDR;
-       if (px->cap & PR_CAP_BE)
-               where |= SMP_VAL_BE_HRQ_HDR;
-
-       if (!(expr->fetch->val & where)) {
-               memprintf(err,
-                         "fetch method '%s' extracts information from '%s', none of which is available here",
-                         args[cur_arg-1], sample_src_names(expr->fetch->use));
-               free(expr);
-               return ACT_RET_PRS_ERR;
-       }
-       rule->arg.expr = expr;
-       rule->action = ACT_CUSTOM;
-
-       if (!strcmp(args[*orig_arg-1], "set-src")) {
-               rule->action_ptr = tcp_action_req_set_src;
-       } else if (!strcmp(args[*orig_arg-1], "set-src-port")) {
-               rule->action_ptr = tcp_action_req_set_src_port;
-       } else if (!strcmp(args[*orig_arg-1], "set-dst")) {
-               rule->action_ptr = tcp_action_req_set_dst;
-       } else if (!strcmp(args[*orig_arg-1], "set-dst-port")) {
-               rule->action_ptr = tcp_action_req_set_dst_port;
-       } else {
-               return ACT_RET_PRS_ERR;
-       }
-
-       (*orig_arg)++;
-
-       return ACT_RET_PRS_OK;
-}
-
-
-/* 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     */
 /************************************************************************/
@@ -2101,61 +1852,6 @@ static struct srv_kw_list srv_kws = { "TCP", { }, {
 
 INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
 
-static struct action_kw_list tcp_req_conn_actions = {ILH, {
-       { "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 },
-       { "silent-drop",  tcp_parse_silent_drop },
-       { /* END */ }
-}};
-
-INITCALL1(STG_REGISTER, tcp_req_conn_keywords_register, &tcp_req_conn_actions);
-
-static struct action_kw_list tcp_req_sess_actions = {ILH, {
-       { "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 },
-       { "silent-drop",  tcp_parse_silent_drop },
-       { /* END */ }
-}};
-
-INITCALL1(STG_REGISTER, tcp_req_sess_keywords_register, &tcp_req_sess_actions);
-
-static struct action_kw_list tcp_req_cont_actions = {ILH, {
-       { "set-dst"     , tcp_parse_set_src_dst },
-       { "set-dst-port", tcp_parse_set_src_dst },
-       { "silent-drop",  tcp_parse_silent_drop },
-       { /* END */ }
-}};
-
-INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_cont_actions);
-
-static struct action_kw_list tcp_res_cont_actions = {ILH, {
-       { "silent-drop", tcp_parse_silent_drop },
-       { /* END */ }
-}};
-
-INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_cont_actions);
-
-static struct action_kw_list http_req_actions = {ILH, {
-       { "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 },
-       { /* END */ }
-}};
-
-INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions);
-
-static struct action_kw_list http_res_actions = {ILH, {
-       { "silent-drop", tcp_parse_silent_drop },
-       { /* END */ }
-}};
-
-INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_actions);
 
 REGISTER_BUILD_OPTS("Built with transparent proxy support using:"
 #if defined(IP_TRANSPARENT)
diff --git a/src/tcp_act.c b/src/tcp_act.c
new file mode 100644 (file)
index 0000000..182234f
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * AF_INET/AF_INET6 SOCK_STREAM protocol layer (tcp)
+ *
+ * Copyright 2000-2013 Willy Tarreau <w@1wt.eu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+
+#include <haproxy/action-t.h>
+#include <haproxy/api.h>
+#include <haproxy/arg.h>
+#include <haproxy/channel.h>
+#include <haproxy/connection.h>
+#include <haproxy/global.h>
+#include <haproxy/http_rules.h>
+#include <haproxy/proto_tcp.h>
+#include <haproxy/proxy-t.h>
+#include <haproxy/sample.h>
+#include <haproxy/stream-t.h>
+#include <haproxy/tcp_rules.h>
+#include <haproxy/tools.h>
+
+/*
+ * Execute the "set-src" action. May be called from {tcp,http}request.
+ * It only changes the address and tries to preserve the original port. If the
+ * previous family was neither AF_INET nor AF_INET6, the port is set to zero.
+ */
+static enum act_return tcp_action_req_set_src(struct act_rule *rule, struct proxy *px,
+                                              struct session *sess, struct stream *s, int flags)
+{
+       struct connection *cli_conn;
+
+       if ((cli_conn = objt_conn(sess->origin)) && conn_get_src(cli_conn)) {
+               struct sample *smp;
+
+               smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_ADDR);
+               if (smp) {
+                       int port = get_net_port(cli_conn->src);
+
+                       if (smp->data.type == SMP_T_IPV4) {
+                               ((struct sockaddr_in *)cli_conn->src)->sin_family = AF_INET;
+                               ((struct sockaddr_in *)cli_conn->src)->sin_addr.s_addr = smp->data.u.ipv4.s_addr;
+                               ((struct sockaddr_in *)cli_conn->src)->sin_port = port;
+                       } else if (smp->data.type == SMP_T_IPV6) {
+                               ((struct sockaddr_in6 *)cli_conn->src)->sin6_family = AF_INET6;
+                               memcpy(&((struct sockaddr_in6 *)cli_conn->src)->sin6_addr, &smp->data.u.ipv6, sizeof(struct in6_addr));
+                               ((struct sockaddr_in6 *)cli_conn->src)->sin6_port = port;
+                       }
+               }
+               cli_conn->flags |= CO_FL_ADDR_FROM_SET;
+       }
+       return ACT_RET_CONT;
+}
+
+/*
+ * Execute the "set-dst" action. May be called from {tcp,http}request.
+ * It only changes the address and tries to preserve the original port. If the
+ * previous family was neither AF_INET nor AF_INET6, the port is set to zero.
+ */
+static enum act_return tcp_action_req_set_dst(struct act_rule *rule, struct proxy *px,
+                                              struct session *sess, struct stream *s, int flags)
+{
+       struct connection *cli_conn;
+
+       if ((cli_conn = objt_conn(sess->origin)) && conn_get_dst(cli_conn)) {
+               struct sample *smp;
+
+               smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_ADDR);
+               if (smp) {
+                       int port = get_net_port(cli_conn->dst);
+
+                       if (smp->data.type == SMP_T_IPV4) {
+                               ((struct sockaddr_in *)cli_conn->dst)->sin_family = AF_INET;
+                               ((struct sockaddr_in *)cli_conn->dst)->sin_addr.s_addr = smp->data.u.ipv4.s_addr;
+                       } else if (smp->data.type == SMP_T_IPV6) {
+                               ((struct sockaddr_in6 *)cli_conn->dst)->sin6_family = AF_INET6;
+                               memcpy(&((struct sockaddr_in6 *)cli_conn->dst)->sin6_addr, &smp->data.u.ipv6, sizeof(struct in6_addr));
+                               ((struct sockaddr_in6 *)cli_conn->dst)->sin6_port = port;
+                       }
+                       cli_conn->flags |= CO_FL_ADDR_TO_SET;
+               }
+       }
+       return ACT_RET_CONT;
+}
+
+/*
+ * Execute the "set-src-port" action. May be called from {tcp,http}request.
+ * We must test the sin_family before setting the port. If the address family
+ * is neither AF_INET nor AF_INET6, the address is forced to AF_INET "0.0.0.0"
+ * and the port is assigned.
+ */
+static enum act_return tcp_action_req_set_src_port(struct act_rule *rule, struct proxy *px,
+                                                   struct session *sess, struct stream *s, int flags)
+{
+       struct connection *cli_conn;
+
+       if ((cli_conn = objt_conn(sess->origin)) && conn_get_src(cli_conn)) {
+               struct sample *smp;
+
+               smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_SINT);
+               if (smp) {
+                       if (cli_conn->src->ss_family == AF_INET6) {
+                               ((struct sockaddr_in6 *)cli_conn->src)->sin6_port = htons(smp->data.u.sint);
+                       } else {
+                               if (cli_conn->src->ss_family != AF_INET) {
+                                       cli_conn->src->ss_family = AF_INET;
+                                       ((struct sockaddr_in *)cli_conn->src)->sin_addr.s_addr = 0;
+                               }
+                               ((struct sockaddr_in *)cli_conn->src)->sin_port = htons(smp->data.u.sint);
+                       }
+               }
+       }
+       return ACT_RET_CONT;
+}
+
+/*
+ * Execute the "set-dst-port" action. May be called from {tcp,http}request.
+ * We must test the sin_family before setting the port. If the address family
+ * is neither AF_INET nor AF_INET6, the address is forced to AF_INET "0.0.0.0"
+ * and the port is assigned.
+ */
+static enum act_return tcp_action_req_set_dst_port(struct act_rule *rule, struct proxy *px,
+                                                   struct session *sess, struct stream *s, int flags)
+{
+       struct connection *cli_conn;
+
+       if ((cli_conn = objt_conn(sess->origin)) && conn_get_dst(cli_conn)) {
+               struct sample *smp;
+
+               smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_SINT);
+               if (smp) {
+                       if (cli_conn->dst->ss_family == AF_INET6) {
+                               ((struct sockaddr_in6 *)cli_conn->dst)->sin6_port = htons(smp->data.u.sint);
+                       } else {
+                               if (cli_conn->dst->ss_family != AF_INET) {
+                                       cli_conn->dst->ss_family = AF_INET;
+                                       ((struct sockaddr_in *)cli_conn->dst)->sin_addr.s_addr = 0;
+                               }
+                               ((struct sockaddr_in *)cli_conn->dst)->sin_port = htons(smp->data.u.sint);
+                       }
+               }
+       }
+       return ACT_RET_CONT;
+}
+
+/* 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;
+
+#ifdef TCP_QUICKACK
+       /* drain is needed only to send the quick ACK */
+       conn_sock_drain(conn);
+
+       /* 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->handle.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;
+
+       /* We're on the client-facing side, we must force to disable lingering to
+        * ensure we will use an RST exclusively and kill any pending data.
+        */
+       fdtab[conn->handle.fd].linger_risk = 1;
+
+#ifdef TCP_REPAIR
+       if (setsockopt(conn->handle.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->handle.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 &= AN_REQ_FLT_END;
+               strm->res.analysers &= AN_RES_FLT_END;
+               if (strm->flags & SF_BE_ASSIGNED)
+                       _HA_ATOMIC_ADD(&strm->be->be_counters.denied_req, 1);
+               if (!(strm->flags & SF_ERR_MASK))
+                       strm->flags |= SF_ERR_PRXCOND;
+               if (!(strm->flags & SF_FINST_MASK))
+                       strm->flags |= SF_FINST_R;
+       }
+
+       _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
+       if (sess->listener->counters)
+               _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
+
+       return ACT_RET_ABRT;
+}
+
+/* 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)
+{
+       int cur_arg;
+       struct sample_expr *expr;
+       unsigned int where;
+
+       cur_arg = *orig_arg;
+       expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line, err, &px->conf.args, NULL);
+       if (!expr)
+               return ACT_RET_PRS_ERR;
+
+       where = 0;
+       if (px->cap & PR_CAP_FE)
+               where |= SMP_VAL_FE_HRQ_HDR;
+       if (px->cap & PR_CAP_BE)
+               where |= SMP_VAL_BE_HRQ_HDR;
+
+       if (!(expr->fetch->val & where)) {
+               memprintf(err,
+                         "fetch method '%s' extracts information from '%s', none of which is available here",
+                         args[cur_arg-1], sample_src_names(expr->fetch->use));
+               free(expr);
+               return ACT_RET_PRS_ERR;
+       }
+       rule->arg.expr = expr;
+       rule->action = ACT_CUSTOM;
+
+       if (!strcmp(args[*orig_arg-1], "set-src")) {
+               rule->action_ptr = tcp_action_req_set_src;
+       } else if (!strcmp(args[*orig_arg-1], "set-src-port")) {
+               rule->action_ptr = tcp_action_req_set_src_port;
+       } else if (!strcmp(args[*orig_arg-1], "set-dst")) {
+               rule->action_ptr = tcp_action_req_set_dst;
+       } else if (!strcmp(args[*orig_arg-1], "set-dst-port")) {
+               rule->action_ptr = tcp_action_req_set_dst_port;
+       } else {
+               return ACT_RET_PRS_ERR;
+       }
+
+       (*orig_arg)++;
+
+       return ACT_RET_PRS_OK;
+}
+
+
+/* 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;
+}
+
+
+static struct action_kw_list tcp_req_conn_actions = {ILH, {
+       { "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 },
+       { "silent-drop",  tcp_parse_silent_drop },
+       { /* END */ }
+}};
+
+INITCALL1(STG_REGISTER, tcp_req_conn_keywords_register, &tcp_req_conn_actions);
+
+static struct action_kw_list tcp_req_sess_actions = {ILH, {
+       { "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 },
+       { "silent-drop",  tcp_parse_silent_drop },
+       { /* END */ }
+}};
+
+INITCALL1(STG_REGISTER, tcp_req_sess_keywords_register, &tcp_req_sess_actions);
+
+static struct action_kw_list tcp_req_cont_actions = {ILH, {
+       { "set-dst"     , tcp_parse_set_src_dst },
+       { "set-dst-port", tcp_parse_set_src_dst },
+       { "silent-drop",  tcp_parse_silent_drop },
+       { /* END */ }
+}};
+
+INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_cont_actions);
+
+static struct action_kw_list tcp_res_cont_actions = {ILH, {
+       { "silent-drop", tcp_parse_silent_drop },
+       { /* END */ }
+}};
+
+INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_cont_actions);
+
+static struct action_kw_list http_req_actions = {ILH, {
+       { "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 },
+       { /* END */ }
+}};
+
+INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions);
+
+static struct action_kw_list http_res_actions = {ILH, {
+       { "silent-drop", tcp_parse_silent_drop },
+       { /* END */ }
+}};
+
+INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_actions);
+
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ */