]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: tcp-act: parse 'tcp-request attach-srv' session rule
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 25 Jul 2023 13:59:30 +0000 (15:59 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 24 Aug 2023 13:02:32 +0000 (15:02 +0200)
Create a new tcp-request session rule 'attach-srv'.

The parsing handler is used to extract the server targetted with the
notation 'backend/server'. The server instance is stored in the act_rule
instance under the new union variant 'attach_srv'.

Extra checks are implemented in parsing to ensure attach-srv is only
used for proxy in HTTP mode and with listeners/server with no explicit
protocol reference or HTTP/2 only.

The action handler itself is really simple. It assigns the stored server
instance to the 'reverse' member of the connection instance. It will be
used in a future patch to implement passive reverse-connect.

doc/configuration.txt
include/haproxy/action-t.h
include/haproxy/connection.h
src/tcp_act.c

index 5ebd2a5b271903ce3d3b5d7615c8bec2452a15f2..9b5ab25cc667c55db9a1e2a67a325c88184d0e94 100644 (file)
@@ -13825,6 +13825,7 @@ tcp-request session <action> [{if | unless} <condition>]
   The first keyword is the rule's action. Several types of actions are
   supported:
     - accept
+    - attach-srv <srv>
     - reject
     - sc-add-gpc(<idx>,<sc-id>) { <int> | <expr> }
     - sc-inc-gpc(<idx>,<sc-id>)
@@ -13893,6 +13894,16 @@ tcp-request session accept [ { if | unless } <condition> ]
   This is used to accept the connection. No further "tcp-request session"
   rules are evaluated.
 
+tcp-request session attach-srv <srv>
+
+  This is used to intercept the connection after proper HTTP/2 establishment.
+  The connection is reversed to the backend side and inserted into the idle
+  pool of <srv> server. This is useful for reverse server with '@reverse'
+  address.
+
+  This rule is only valid for frontend in HTTP mode. Also all listeners must
+  not require a protocol different from HTTP/2.
+
 tcp-request session reject [ { if | unless } <condition> ]
 
   This is used to reject the connection. No further "tcp-request session" rules
index 2745ba7b4f7344c56ad86625737af9e76b72f82a..4237527bbf3215568fa8f66af9b9d975cdb65e87 100644 (file)
@@ -185,6 +185,10 @@ struct act_rule {
                        struct sample_expr *expr;
                } gpt;
                struct track_ctr_prm trk_ctr;
+               struct {
+                       char *srvname; /* server name from config parsing. */
+                       struct server *srv; /* target server to attach the connection */
+               } attach_srv; /* 'attach-srv' rule */
                struct {
                        void *p[4];
                } act;                         /* generic pointers to be used by custom actions */
index 88dfd8f8d39036721960f8b7eb3bf547eb089915..a0af6334c70e095a92eb0b214772479d34058021 100644 (file)
@@ -703,6 +703,15 @@ static inline int conn_is_reverse(const struct connection *conn)
        return !!(conn->reverse.target);
 }
 
+/* Initialize <conn> as a reverse connection to <target>. */
+static inline void conn_set_reverse(struct connection *conn, enum obj_type *target)
+{
+       /* Ensure the correct target type is used depending on the connection side before reverse. */
+       BUG_ON(!conn_is_back(conn) && !objt_server(target));
+
+       conn->reverse.target = target;
+}
+
 #endif /* _HAPROXY_CONNECTION_H */
 
 /*
index 05ed05695b759b16cfa48a0f8ab0af108c563e1d..9522e0f4991d23d746548ed4e6e26a0753d5de10 100644 (file)
 #include <haproxy/global.h>
 #include <haproxy/http_rules.h>
 #include <haproxy/proto_tcp.h>
-#include <haproxy/proxy-t.h>
+#include <haproxy/proxy.h>
 #include <haproxy/sample.h>
 #include <haproxy/sc_strm.h>
+#include <haproxy/server.h>
 #include <haproxy/session.h>
 #include <haproxy/tcp_rules.h>
 #include <haproxy/tools.h>
 
+static enum act_return tcp_action_attach_srv(struct act_rule *rule, struct proxy *px,
+                                             struct session *sess, struct stream *s, int flags)
+{
+       struct server *srv = rule->arg.attach_srv.srv;
+       struct connection *conn = objt_conn(sess->origin);
+       if (!conn)
+               return ACT_RET_ABRT;
+
+       conn_set_reverse(conn, &srv->obj_type);
+       return ACT_RET_CONT;
+}
+
 /*
  * 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
@@ -374,6 +387,14 @@ static enum act_return tcp_action_set_tos(struct act_rule *rule, struct proxy *p
 }
 #endif
 
+/*
+ * Release the sample expr when releasing attach-srv action
+ */
+static void release_attach_srv_action(struct act_rule *rule)
+{
+       ha_free(&rule->arg.attach_srv.srvname);
+}
+
 /*
  * Release the sample expr when releasing a set src/dst action
  */
@@ -382,6 +403,87 @@ static void release_set_src_dst_action(struct act_rule *rule)
        release_sample_expr(rule->arg.expr);
 }
 
+static int tcp_check_attach_srv(struct act_rule *rule, struct proxy *px, char **err)
+{
+       struct proxy *be = NULL;
+       struct server *srv = NULL;
+       struct bind_conf *bind_conf;
+       char *name = rule->arg.attach_srv.srvname;
+       struct ist be_name, sv_name;
+
+       if (px->mode != PR_MODE_HTTP) {
+               memprintf(err, "attach-srv rule requires HTTP proxy mode");
+               return 0;
+       }
+
+       list_for_each_entry(bind_conf, &px->conf.bind, by_fe) {
+               if ((bind_conf->mux_proto && !isteqi(bind_conf->mux_proto->token, ist("h2")))
+#ifdef USE_OPENSSL
+                   || (bind_conf->ssl_conf.alpn_str && strcmp(bind_conf->ssl_conf.alpn_str, "\x02h2") != 0)
+#endif
+               ) {
+                       memprintf(err, "attach-srv rule: incompatible with listener on %s:%d which uses protocol other than HTTP/2",
+                                 bind_conf->file, bind_conf->line);
+                       return 0;
+               }
+       }
+
+       sv_name = ist(name);
+       be_name = istsplit(&sv_name, '/');
+       if (!istlen(sv_name)) {
+               memprintf(err, "attach-srv rule: invalid server name '%s'", name);
+               return 0;
+       }
+
+       if (!(be = proxy_be_by_name(ist0(be_name)))) {
+               memprintf(err, "attach-srv rule: no such backend '%s/%s'", ist0(be_name), ist0(sv_name));
+               return 0;
+       }
+       if (!(srv = server_find_by_name(be, ist0(sv_name)))) {
+               memprintf(err, "attach-srv rule: no such server '%s/%s'", ist0(be_name), ist0(sv_name));
+               return 0;
+       }
+
+       if ((srv->mux_proto && !isteqi(srv->mux_proto->token, ist("h2")))
+#ifdef USE_OPENSSL
+                   || (srv->ssl_ctx.alpn_str && strcmp(srv->ssl_ctx.alpn_str, "\x02h2") != 0)
+#endif
+       ) {
+               memprintf(err, "attach-srv rule: incompatible with server '%s:%s' which uses protocol other than HTTP/2",
+                         ist0(be_name), ist0(sv_name));
+               return 0;
+       }
+
+       rule->arg.attach_srv.srv = srv;
+
+       return 1;
+}
+
+static enum act_parse_ret tcp_parse_attach_srv(const char **args, int *cur_arg, struct proxy *px,
+                                               struct act_rule *rule, char **err)
+{
+       char *srvname;
+
+       rule->action      = ACT_CUSTOM;
+       rule->action_ptr  = tcp_action_attach_srv;
+       rule->release_ptr = release_attach_srv_action;
+       rule->check_ptr   = tcp_check_attach_srv;
+       rule->arg.attach_srv.srvname = NULL;
+
+       srvname = my_strndup(args[*cur_arg], strlen(args[*cur_arg]));
+       if (!srvname)
+               goto err;
+       rule->arg.attach_srv.srvname = srvname;
+
+       ++(*cur_arg);
+
+       return ACT_RET_PRS_OK;
+
+ err:
+       ha_free(&rule->arg.attach_srv.srvname);
+       return ACT_RET_PRS_ERR;
+}
+
 /* 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)
@@ -551,6 +653,7 @@ static struct action_kw_list tcp_req_conn_actions = {ILH, {
 INITCALL1(STG_REGISTER, tcp_req_conn_keywords_register, &tcp_req_conn_actions);
 
 static struct action_kw_list tcp_req_sess_actions = {ILH, {
+       { "attach-srv"  , tcp_parse_attach_srv  },
        { "set-dst"     , tcp_parse_set_src_dst },
        { "set-dst-port", tcp_parse_set_src_dst },
        { "set-mark",     tcp_parse_set_mark    },