From 58cb76d7e178bb1008a246c314ffea5a4f8df5e5 Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Tue, 25 Jul 2023 15:59:30 +0200 Subject: [PATCH] MINOR: tcp-act: parse 'tcp-request attach-srv' session rule 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 | 11 ++++ include/haproxy/action-t.h | 4 ++ include/haproxy/connection.h | 9 +++ src/tcp_act.c | 105 ++++++++++++++++++++++++++++++++++- 4 files changed, 128 insertions(+), 1 deletion(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 5ebd2a5b2..9b5ab25cc 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -13825,6 +13825,7 @@ tcp-request session [{if | unless} ] The first keyword is the rule's action. Several types of actions are supported: - accept + - attach-srv - reject - sc-add-gpc(,) { | } - sc-inc-gpc(,) @@ -13893,6 +13894,16 @@ tcp-request session accept [ { if | unless } ] This is used to accept the connection. No further "tcp-request session" rules are evaluated. +tcp-request session attach-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 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 } ] This is used to reject the connection. No further "tcp-request session" rules diff --git a/include/haproxy/action-t.h b/include/haproxy/action-t.h index 2745ba7b4..4237527bb 100644 --- a/include/haproxy/action-t.h +++ b/include/haproxy/action-t.h @@ -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 */ diff --git a/include/haproxy/connection.h b/include/haproxy/connection.h index 88dfd8f8d..a0af6334c 100644 --- a/include/haproxy/connection.h +++ b/include/haproxy/connection.h @@ -703,6 +703,15 @@ static inline int conn_is_reverse(const struct connection *conn) return !!(conn->reverse.target); } +/* Initialize as a reverse connection to . */ +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 */ /* diff --git a/src/tcp_act.c b/src/tcp_act.c index 05ed05695..9522e0f49 100644 --- a/src/tcp_act.c +++ b/src/tcp_act.c @@ -32,13 +32,26 @@ #include #include #include -#include +#include #include #include +#include #include #include #include +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 },