]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: proto_reverse_connect: parse rev@ addresses for bind
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 7 Aug 2023 15:56:36 +0000 (17:56 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 24 Aug 2023 15:02:37 +0000 (17:02 +0200)
Implement parsing for "rev@" addresses on bind line. On config parsing,
server name is stored on the bind_conf.

Several new callbacks are defined on reverse_connect protocol to
complete parsing. listen callback is used to retrieve the server
instance from the bind_conf server name. If found, the server instance
is stored on the receiver. Checks are implemented to ensure HTTP/2
protocol only is used by the server.

doc/configuration.txt
include/haproxy/listener-t.h
include/haproxy/proto_reverse_connect.h
include/haproxy/receiver-t.h
src/cfgparse.c
src/listener.c
src/proto_reverse_connect.c
src/proxy.c

index 9626b4c995ad80ad2be9cb7d26c11cb87973f11b..cde951a1f61b45ba6891fbb67dce4e253af5c57c 100644 (file)
@@ -4993,6 +4993,10 @@ bind /<path> [, ...] [param*]
                     - 'quic6@' -> address is resolved as IPv6 and protocol UDP
                       is used. The performance note for QUIC over IPv4 applies
                       as well.
+                    - 'rev@' -> used for reverse HTTP. Address must be a server
+                      with the format '<backend>/<server>'. The server will be
+                      used to instantiate connections to a remote address. The
+                      listener will try to maintain 'maxconn' connections.
 
                   You may want to reference some environment variables in the
                   address parameter, see section 2.3 about environment
index 7384739be17f613d8f4dc177ee61c59f2bcfe6fb..8e02799a41e037534f767c5a5eac45b0e7ffb8c1 100644 (file)
@@ -205,6 +205,7 @@ struct bind_conf {
        char *arg;                 /* argument passed to "bind" for better error reporting */
        char *file;                /* file where the section appears */
        int line;                  /* line where the section appears */
+       char *reverse_srvname;     /* name of server when using "rev@" address */
        __decl_thread(HA_RWLOCK_T sni_lock); /* lock the SNI trees during add/del operations */
        struct thread_set thread_set; /* entire set of the allowed threads (0=no restriction) */
        struct rx_settings settings; /* all the settings needed for the listening socket */
index 31edbb794a1a97ec4fbe4883531c2a6c6f1efc74..2fdf4b1d468ddc6fb1ef4c42c9e46618bb9ded86 100644 (file)
@@ -7,6 +7,7 @@
 int rev_bind_receiver(struct receiver *rx, char **errmsg);
 
 int rev_bind_listener(struct listener *listener, char *errmsg, int errlen);
+void rev_unbind_receiver(struct listener *l);
 
 int rev_accepting_conn(const struct receiver *rx);
 
index fcf43504d51e91247d557713f8b6951b6468e118..87dc61e342db0684feb759e7239a311e17da8e37 100644 (file)
@@ -79,6 +79,10 @@ struct receiver {
 #ifdef USE_QUIC
        struct mt_list rxbuf_list;       /* list of buffers to receive and dispatch QUIC datagrams. */
 #endif
+       struct {
+               struct server *srv; /* Underlying server used to initiate reverse pre-connect. */
+       } reverse_connect;
+
        /* warning: this struct is huge, keep it at the bottom */
        struct sockaddr_storage addr;    /* the address the socket is bound to */
 };
index 28e04b50f1e3d46de793a8adcd807b67025237c7..04da22bfaea00bf3d540c2ae65f4aafe8b7608c7 100644 (file)
@@ -173,6 +173,10 @@ int str2listener(char *str, struct proxy *curproxy, struct bind_conf *bind_conf,
                else
                        bind_conf->options |= BC_O_USE_XPRT_STREAM;
 
+               if (ss2->ss_family == AF_CUST_REV_SRV) {
+                       bind_conf->reverse_srvname = strdup(str + strlen("rev@"));
+               }
+
                if (!create_listeners(bind_conf, ss2, port, end, fd, proto, err)) {
                        memprintf(err, "%s for address '%s'.\n", *err, str);
                        goto fail;
index 70d13ea84333423058b06f1408e238d3f6c72d9b..14c2b05cbb6c66c171f93d01b28ff8041f2cb366 100644 (file)
@@ -799,6 +799,8 @@ int create_listeners(struct bind_conf *bc, const struct sockaddr_storage *ss,
                l->rx.iocb = proto->default_iocb;
                l->rx.fd = fd;
 
+               l->rx.reverse_connect.srv = NULL;
+
                memcpy(&l->rx.addr, ss, sizeof(*ss));
                if (proto->fam->set_port)
                        proto->fam->set_port(&l->rx.addr, port);
@@ -1945,6 +1947,9 @@ struct bind_conf *bind_conf_alloc(struct proxy *fe, const char *file,
        bind_conf->sni_w_ctx = EB_ROOT;
 #endif
        LIST_INIT(&bind_conf->listeners);
+
+       bind_conf->reverse_srvname = NULL;
+
        return bind_conf;
 
   err:
index 621f328715ba5663e7b9434812f6e29037ca8208..d081482d4a4d7be7e03830803b92ad1e2eb7e3c1 100644 (file)
@@ -1,8 +1,13 @@
+#include <stdio.h>
+#include <string.h>
+
 #include <haproxy/api.h>
 #include <haproxy/errors.h>
 #include <haproxy/list.h>
 #include <haproxy/listener.h>
 #include <haproxy/protocol.h>
+#include <haproxy/proxy.h>
+#include <haproxy/server.h>
 
 #include <haproxy/proto_reverse_connect.h>
 
@@ -17,8 +22,10 @@ struct protocol proto_reverse_connect = {
        .name = "rev",
 
        /* connection layer */
-       .listen  = rev_bind_listener,
-       .add     = default_add_listener,
+       .listen      = rev_bind_listener,
+       .unbind      = rev_unbind_receiver,
+       .add         = default_add_listener,
+       .resume      = default_resume_listener,
 
        /* address family */
        .fam  = &proto_fam_reverse_connect,
@@ -33,12 +40,65 @@ struct protocol proto_reverse_connect = {
 
 int rev_bind_receiver(struct receiver *rx, char **errmsg)
 {
+       rx->flags |= RX_F_BOUND;
        return ERR_NONE;
 }
 
 int rev_bind_listener(struct listener *listener, char *errmsg, int errlen)
 {
+       struct proxy *be;
+       struct server *srv;
+       struct ist be_name, sv_name;
+       char *name = NULL;
+
+       name = strdup(listener->bind_conf->reverse_srvname);
+       if (!name) {
+               snprintf(errmsg, errlen, "Out of memory.");
+               goto err;
+       }
+
+       sv_name = ist(name);
+       be_name = istsplit(&sv_name, '/');
+       if (!istlen(sv_name)) {
+               snprintf(errmsg, errlen, "Invalid server name: '%s'.", name);
+               goto err;
+       }
+
+       if (!(be = proxy_be_by_name(ist0(be_name)))) {
+               snprintf(errmsg, errlen, "No such backend: '%s'.", name);
+               goto err;
+       }
+       if (!(srv = server_find_by_name(be, ist0(sv_name)))) {
+               snprintf(errmsg, errlen, "No such server: '%s/%s'.", ist0(be_name), ist0(sv_name));
+               goto err;
+       }
+
+       /* TODO check que on utilise pas un serveur @reverse */
+       if (srv->flags & SRV_F_REVERSE) {
+               snprintf(errmsg, errlen, "Cannot use reverse server '%s/%s' as target to a reverse bind.", ist0(be_name), ist0(sv_name));
+               goto err;
+       }
+
+       /* Check that server uses HTTP/2 either with proto or ALPN. */
+       if ((!srv->mux_proto || !isteqi(srv->mux_proto->token, ist("h2"))) &&
+           (!srv->use_ssl || !isteqi(ist(srv->ssl_ctx.alpn_str), ist("\x02h2")))) {
+               snprintf(errmsg, errlen, "Cannot reverse connect with server '%s/%s' unless HTTP/2 is activated on it with either proto or alpn keyword.", name, ist0(sv_name));
+               goto err;
+       }
+       ha_free(&name);
+
+       listener->rx.reverse_connect.srv = srv;
+
        return ERR_NONE;
+
+ err:
+       ha_free(&name);
+       return ERR_ALERT | ERR_FATAL;
+}
+
+void rev_unbind_receiver(struct listener *l)
+{
+       l->rx.flags &= ~RX_F_BOUND;
 }
 
 int rev_accepting_conn(const struct receiver *rx)
index ad94802fa9c6b67d50af032980c4f56140368e0c..89e673d94e1c6d28a2e175a7e39dc7dbd8555206 100644 (file)
@@ -334,6 +334,7 @@ void free_proxy(struct proxy *p)
                free(bind_conf->arg);
                free(bind_conf->settings.interface);
                LIST_DELETE(&bind_conf->by_fe);
+               free(bind_conf->reverse_srvname);
                free(bind_conf);
        }