diff --git a/doc/configuration.txt b/doc/configuration.txt index 9626b4c99..cde951a1f 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -4993,6 +4993,10 @@ bind / [, ...] [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 '/'. 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 diff --git a/include/haproxy/listener-t.h b/include/haproxy/listener-t.h index 7384739be..8e02799a4 100644 --- a/include/haproxy/listener-t.h +++ b/include/haproxy/listener-t.h @@ -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 */ diff --git a/include/haproxy/proto_reverse_connect.h b/include/haproxy/proto_reverse_connect.h index 31edbb794..2fdf4b1d4 100644 --- a/include/haproxy/proto_reverse_connect.h +++ b/include/haproxy/proto_reverse_connect.h @@ -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); diff --git a/include/haproxy/receiver-t.h b/include/haproxy/receiver-t.h index fcf43504d..87dc61e34 100644 --- a/include/haproxy/receiver-t.h +++ b/include/haproxy/receiver-t.h @@ -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 */ }; diff --git a/src/cfgparse.c b/src/cfgparse.c index 28e04b50f..04da22bfa 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -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; diff --git a/src/listener.c b/src/listener.c index 70d13ea84..14c2b05cb 100644 --- a/src/listener.c +++ b/src/listener.c @@ -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: diff --git a/src/proto_reverse_connect.c b/src/proto_reverse_connect.c index 621f32871..d081482d4 100644 --- a/src/proto_reverse_connect.c +++ b/src/proto_reverse_connect.c @@ -1,8 +1,13 @@ +#include +#include + #include #include #include #include #include +#include +#include #include @@ -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) diff --git a/src/proxy.c b/src/proxy.c index ad94802fa..89e673d94 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -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); }