diff --git a/include/proto/proto_tcp.h b/include/proto/proto_tcp.h index b93b37145..ba48f4ff1 100644 --- a/include/proto/proto_tcp.h +++ b/include/proto/proto_tcp.h @@ -1,23 +1,23 @@ /* - include/proto/proto_tcp.h - This file contains TCP socket protocol definitions. - - Copyright (C) 2000-2008 Willy Tarreau - w@1wt.eu - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation, version 2.1 - exclusively. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ + * include/proto/proto_tcp.h + * This file contains TCP socket protocol definitions. + * + * Copyright (C) 2000-2010 Willy Tarreau - w@1wt.eu + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, version 2.1 + * exclusively. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ #ifndef _PROTO_PROTO_TCP_H #define _PROTO_PROTO_TCP_H @@ -33,7 +33,7 @@ void tcpv6_add_listener(struct listener *listener); int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen); int tcpv4_connect_server(struct stream_interface *si, struct proxy *be, struct server *srv, - struct sockaddr *srv_addr, struct sockaddr *cli_addr); + struct sockaddr *srv_addr, struct sockaddr *from_addr); int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit); int acl_fetch_rdp_cookie(struct proxy *px, struct session *l4, void *l7, int dir, struct acl_expr *expr, struct acl_test *test); diff --git a/include/types/session.h b/include/types/session.h index 52587503b..94bb5d5d8 100644 --- a/include/types/session.h +++ b/include/types/session.h @@ -170,6 +170,7 @@ struct session { struct sockaddr_storage cli_addr; /* the client address */ struct sockaddr_storage frt_addr; /* the frontend address reached by the client if SN_FRT_ADDR_SET is set */ struct sockaddr_in srv_addr; /* the address to connect to */ + struct sockaddr_in from_addr; /* the address to spoof when connecting to the server (transparent mode) */ struct server *srv; /* the server the session will be running or has been running on */ struct server *srv_conn; /* session already has a slot on a server and is not in queue */ struct server *prev_srv; /* the server the was running on, after a redispatch, otherwise NULL */ diff --git a/src/backend.c b/src/backend.c index 1a8653762..845a436bd 100644 --- a/src/backend.c +++ b/src/backend.c @@ -820,6 +820,45 @@ int assign_server_and_queue(struct session *s) } } +/* If an explicit source binding is specified on the server and/or backend, and + * this source makes use of the transparent proxy, then it is extracted now and + * assigned to the session's from_addr entry. + */ +static void assign_tproxy_address(struct session *s) +{ +#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) + if (s->srv != NULL && s->srv->state & SRV_BIND_SRC) { + switch (s->srv->state & SRV_TPROXY_MASK) { + case SRV_TPROXY_ADDR: + s->from_addr = *(struct sockaddr_in *)&s->srv->tproxy_addr; + break; + case SRV_TPROXY_CLI: + case SRV_TPROXY_CIP: + /* FIXME: what can we do if the client connects in IPv6 ? */ + s->from_addr = *(struct sockaddr_in *)&s->cli_addr; + break; + default: + s->from_addr = *(struct sockaddr_in *)0; + } + } + else if (s->be->options & PR_O_BIND_SRC) { + switch (s->be->options & PR_O_TPXY_MASK) { + case PR_O_TPXY_ADDR: + s->from_addr = *(struct sockaddr_in *)&s->be->tproxy_addr; + break; + case PR_O_TPXY_CLI: + case PR_O_TPXY_CIP: + /* FIXME: what can we do if the client connects in IPv6 ? */ + s->from_addr = *(struct sockaddr_in *)&s->cli_addr; + break; + default: + s->from_addr = *(struct sockaddr_in *)0; + } + } +#endif +} + + /* * This function initiates a connection to the server assigned to this session * (s->srv, s->srv_addr). It will assign a server if none is assigned yet. @@ -845,9 +884,11 @@ int connect_server(struct session *s) if (!s->req->cons->connect) return SN_ERR_INTERNAL; + assign_tproxy_address(s); + err = s->req->cons->connect(s->req->cons, s->be, s->srv, (struct sockaddr *)&s->srv_addr, - (struct sockaddr *)&s->cli_addr); + (struct sockaddr *)&s->from_addr); if (err != SN_ERR_NONE) return err; diff --git a/src/proto_tcp.c b/src/proto_tcp.c index a62bea8a4..50ab08170 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -180,7 +180,11 @@ int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct socka /* * This function initiates a connection to the server assigned to this session - * (s->srv, s->srv_addr). It will assign a server if none is assigned yet. + * (s->srv, s->srv_addr). It will assign a server if none is assigned yet. A + * source address may be pointed to by . Note that this is only used + * in case of transparent proxying. Normal source bind addresses are still + * determined locally (due to the possible need of a source port). + * * It can return one of : * - SN_ERR_NONE if everything's OK * - SN_ERR_SRVTO if there are no more servers @@ -192,7 +196,7 @@ int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct socka */ int tcpv4_connect_server(struct stream_interface *si, struct proxy *be, struct server *srv, - struct sockaddr *srv_addr, struct sockaddr *cli_addr) + struct sockaddr *srv_addr, struct sockaddr *from_addr) { int fd; @@ -242,27 +246,18 @@ int tcpv4_connect_server(struct stream_interface *si, * - proxy-specific next */ if (srv != NULL && srv->state & SRV_BIND_SRC) { - struct sockaddr_in *remote = NULL; int ret, flags = 0; -#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) switch (srv->state & SRV_TPROXY_MASK) { case SRV_TPROXY_ADDR: - remote = (struct sockaddr_in *)&srv->tproxy_addr; - flags = 3; - break; case SRV_TPROXY_CLI: - if (cli_addr) - flags |= 2; - /* fall through */ + flags = 3; + break; case SRV_TPROXY_CIP: - /* FIXME: what can we do if the client connects in IPv6 ? */ - if (cli_addr) - flags |= 1; - remote = (struct sockaddr_in *)cli_addr; + flags = 1; break; } -#endif + #ifdef SO_BINDTODEVICE /* Note: this might fail if not CAP_NET_RAW */ if (srv->iface_name) @@ -294,11 +289,11 @@ int tcpv4_connect_server(struct stream_interface *si, fdinfo[fd].port_range = srv->sport_range; src.sin_port = htons(fdinfo[fd].local_port); - ret = tcpv4_bind_socket(fd, flags, &src, remote); + ret = tcpv4_bind_socket(fd, flags, &src, (struct sockaddr_in *)from_addr); } while (ret != 0); /* binding NOK */ } else { - ret = tcpv4_bind_socket(fd, flags, &srv->source_addr, remote); + ret = tcpv4_bind_socket(fd, flags, &srv->source_addr, (struct sockaddr_in *)from_addr); } if (ret) { @@ -323,33 +318,24 @@ int tcpv4_connect_server(struct stream_interface *si, } } else if (be->options & PR_O_BIND_SRC) { - struct sockaddr_in *remote = NULL; int ret, flags = 0; -#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) switch (be->options & PR_O_TPXY_MASK) { case PR_O_TPXY_ADDR: - remote = (struct sockaddr_in *)&be->tproxy_addr; - flags = 3; - break; case PR_O_TPXY_CLI: - if (cli_addr) - flags |= 2; - /* fall through */ + flags = 3; + break; case PR_O_TPXY_CIP: - /* FIXME: what can we do if the client connects in IPv6 ? */ - if (cli_addr) - flags |= 1; - remote = (struct sockaddr_in *)cli_addr; + flags = 1; break; } -#endif + #ifdef SO_BINDTODEVICE /* Note: this might fail if not CAP_NET_RAW */ if (be->iface_name) setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, be->iface_name, be->iface_len + 1); #endif - ret = tcpv4_bind_socket(fd, flags, &be->source_addr, remote); + ret = tcpv4_bind_socket(fd, flags, &be->source_addr, (struct sockaddr_in *)from_addr); if (ret) { close(fd); if (ret == 1) {