On Tue, Apr 03, 2012 at 12:17:45AM -0700, Aman Gupta wrote:
> On Tue, Apr 3, 2012 at 12:10 AM, "Brane F. Gra??nar"
> <brane.grac...@tsmedia.si> wrote:
> > On 04/03/2012 08:21 AM, Willy Tarreau wrote:
> >> The protocol is described here :
> >>
> >>       http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt
> >>
> >> Please let me know whether you're interested, then I'll check where I left
> >> the patch :-)
> >
> > Here ya go :)
> 
> Thanks, but this appears to be the accept-proxy side of the
> implementation. I don't see any send-proxy code.

Indeed, just checked and we only had the accept-proxy backport.

I've just done the send-proxy backport. Adapted the code to make
it build. I think it should be correct but I have not tested it
at all. Here are the patches. Please report if something appears
broken so that we can maintain the backport for users who need
this.

Willy

>From 43c33e22c6e1b1f3a40fd66f96b5b6fab0512be8 Mon Sep 17 00:00:00 2001
From: Willy Tarreau <w...@1wt.eu>
Date: Tue, 3 Apr 2012 09:29:38 +0200
Subject: [PATCH 1/3] [MINOR] frontend: add a make_proxy_line function

This function will build a PROXY protocol line header from two addresses
(IPv4 or IPv6). AF_UNIX family will be reported as UNKNOWN.
(cherry picked from commit a73fcaf424ee8985abbec1d57b88c3a74f8198c5)
---
 include/proto/client.h |    1 +
 src/client.c           |   79 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 80 insertions(+), 0 deletions(-)

diff --git a/include/proto/client.h b/include/proto/client.h
index 1d368a4..b52001e 100644
--- a/include/proto/client.h
+++ b/include/proto/client.h
@@ -27,6 +27,7 @@
 
 void get_frt_addr(struct session *s);
 int event_accept(int fd);
+int make_proxy_line(char *buf, int buf_len, struct sockaddr_storage *src, 
struct sockaddr_storage *dst);
 
 
 #endif /* _PROTO_CLIENT_H */
diff --git a/src/client.c b/src/client.c
index cac6cd7..76e79b4 100644
--- a/src/client.c
+++ b/src/client.c
@@ -520,6 +520,85 @@ int event_accept(int fd) {
 }
 
 
+/* Makes a PROXY protocol line from the two addresses. The output is sent to
+ * buffer <buf> for a maximum size of <buf_len> (including the trailing zero).
+ * It returns the number of bytes composing this line (including the trailing
+ * LF), or zero in case of failure (eg: not enough space). It supports TCP4,
+ * TCP6 and "UNKNOWN" formats.
+ */
+int make_proxy_line(char *buf, int buf_len, struct sockaddr_storage *src, 
struct sockaddr_storage *dst)
+{
+       int ret = 0;
+
+       if (src->ss_family == dst->ss_family && src->ss_family == AF_INET) {
+               ret = snprintf(buf + ret, buf_len - ret, "PROXY TCP4 ");
+               if (ret >= buf_len)
+                       return 0;
+
+               /* IPv4 src */
+               if (!inet_ntop(src->ss_family, &((struct sockaddr_in 
*)src)->sin_addr, buf + ret, buf_len - ret))
+                       return 0;
+
+               ret += strlen(buf + ret);
+               if (ret >= buf_len)
+                       return 0;
+
+               buf[ret++] = ' ';
+
+               /* IPv4 dst */
+               if (!inet_ntop(dst->ss_family, &((struct sockaddr_in 
*)dst)->sin_addr, buf + ret, buf_len - ret))
+                       return 0;
+
+               ret += strlen(buf + ret);
+               if (ret >= buf_len)
+                       return 0;
+
+               /* source and destination ports */
+               ret += snprintf(buf + ret, buf_len - ret, " %u %u\r\n",
+                               ntohs(((struct sockaddr_in *)src)->sin_port),
+                               ntohs(((struct sockaddr_in *)dst)->sin_port));
+               if (ret >= buf_len)
+                       return 0;
+       }
+       else if (src->ss_family == dst->ss_family && src->ss_family == 
AF_INET6) {
+               ret = snprintf(buf + ret, buf_len - ret, "PROXY TCP6 ");
+               if (ret >= buf_len)
+                       return 0;
+
+               /* IPv6 src */
+               if (!inet_ntop(src->ss_family, &((struct sockaddr_in6 
*)src)->sin6_addr, buf + ret, buf_len - ret))
+                       return 0;
+
+               ret += strlen(buf + ret);
+               if (ret >= buf_len)
+                       return 0;
+
+               buf[ret++] = ' ';
+
+               /* IPv6 dst */
+               if (!inet_ntop(dst->ss_family, &((struct sockaddr_in6 
*)dst)->sin6_addr, buf + ret, buf_len - ret))
+                       return 0;
+
+               ret += strlen(buf + ret);
+               if (ret >= buf_len)
+                       return 0;
+
+               /* source and destination ports */
+               ret += snprintf(buf + ret, buf_len - ret, " %u %u\r\n",
+                               ntohs(((struct sockaddr_in6 *)src)->sin6_port),
+                               ntohs(((struct sockaddr_in6 *)dst)->sin6_port));
+               if (ret >= buf_len)
+                       return 0;
+       }
+       else {
+               /* unknown family combination */
+               ret = snprintf(buf, buf_len, "PROXY UNKNOWN\r\n");
+               if (ret >= buf_len)
+                       return 0;
+       }
+       return ret;
+}
+
 
 /************************************************************************/
 /*             All supported keywords must be declared here.            */
-- 
1.7.2.1.45.g54fbc

>From b4f72ddad84242d8596c2ac50a2191ad27ead411 Mon Sep 17 00:00:00 2001
From: Willy Tarreau <w...@1wt.eu>
Date: Tue, 3 Apr 2012 09:35:15 +0200
Subject: [PATCH 2/3] [MEDIUM] stream_sock: add support for sending the proxy 
protocol header line

Upon connection establishment, stream_sock is now able to send a PROXY
line before sending any data. Since it's possible that the buffer is
already full, and we don't want to allocate a block for that line, we
compute it on-the-fly when we need it. We just store the offset from
which to (re-)send from the end of the line, since it's assumed that
multiple outputs of the same proxy line will be strictly equivalent. In
practice, one call is enough. We just make sure to handle the case where
the first send() would indicate an incomplete output, eventhough it's
very unlikely to ever happen.
(cherry picked from commit b22e55bc8f06b6679aa5e71c3a73c0f61256f960)
---
 include/types/stream_interface.h |    1 +
 src/stream_sock.c                |   41 ++++++++++++++++++++++++++++++++++++-
 2 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/include/types/stream_interface.h b/include/types/stream_interface.h
index 6ad6684..e813e26 100644
--- a/include/types/stream_interface.h
+++ b/include/types/stream_interface.h
@@ -103,6 +103,7 @@ struct stream_interface {
        void *err_loc;          /* commonly the server, NULL when SI_ET_NONE */
        void *private;          /* may be used by any function above */
        unsigned int st0, st1;  /* may be used by any function above */
+       int send_proxy_ofs;     /* <0 = offset to (re)send from the end, >0 = 
send all */
 };
 
 
diff --git a/src/stream_sock.c b/src/stream_sock.c
index ffbd652..1b74f76 100644
--- a/src/stream_sock.c
+++ b/src/stream_sock.c
@@ -544,6 +544,43 @@ static int stream_sock_write_loop(struct stream_interface 
*si, struct buffer *b)
        int retval = 1;
        int ret, max;
 
+       if (unlikely(si->send_proxy_ofs)) {
+               struct session *s = ((struct task *)si->owner)->context;
+
+               /* The target server expects a PROXY line to be sent first.
+                * If the send_proxy_ofs is negative, it corresponds to the
+                * offset to start sending from then end of the proxy string
+                * (which is recomputed every time since it's constant). If
+                * it is positive, it means we have to send from the start.
+                */
+               ret = make_proxy_line(trash, sizeof(trash),
+                                     &s->cli_addr, &s->frt_addr);
+               if (!ret)
+                       return -1;
+
+               if (si->send_proxy_ofs > 0)
+                       si->send_proxy_ofs = -ret; /* first call */
+
+               /* we have to send trash from (ret+sp for -sp bytes) */
+               ret = send(si->fd, trash + ret + si->send_proxy_ofs, 
-si->send_proxy_ofs,
+                          (b->flags & BF_OUT_EMPTY) ? 0 : MSG_MORE);
+               if (ret > 0) {
+                       if (fdtab[si->fd].state == FD_STCONN)
+                               fdtab[si->fd].state = FD_STREADY;
+
+                       si->send_proxy_ofs += ret; /* becomes zero once 
complete */
+                       b->flags |= BF_WRITE_NULL; /* connect() succeeded */
+               }
+               else if (ret == 0 || errno == EAGAIN) {
+                       /* nothing written, we need to poll for write first */
+                       return 0;
+               }
+               else {
+                       /* bad, we got an error */
+                       return -1;
+               }
+       }
+
 #if defined(CONFIG_HAP_LINUX_SPLICE)
        while (b->pipe) {
                ret = splice(b->pipe->cons, NULL, si->fd, NULL, b->pipe->data,
@@ -704,7 +741,7 @@ int stream_sock_write(int fd)
        if (b->flags & BF_SHUTW)
                goto out_wakeup;
 
-       if (likely(!(b->flags & BF_OUT_EMPTY))) {
+       if (likely(!(b->flags & BF_OUT_EMPTY) || si->send_proxy_ofs)) {
                /* OK there are data waiting to be sent */
                retval = stream_sock_write_loop(si, b);
                if (retval < 0)
@@ -1041,7 +1078,7 @@ void stream_sock_chk_snd(struct stream_interface *si)
 
        if (!(si->flags & SI_FL_WAIT_DATA) ||        /* not waiting for data */
            (fdtab[si->fd].ev & FD_POLL_OUT) ||      /* we'll be called anyway 
*/
-           (ob->flags & BF_OUT_EMPTY))              /* called with nothing to 
send ! */
+           ((ob->flags & BF_OUT_EMPTY) && !(si->send_proxy_ofs)))  /* called 
with nothing to send ! */
                return;
 
        retval = stream_sock_write_loop(si, ob);
-- 
1.7.2.1.45.g54fbc

>From b122bdfb69665aa35aacd69681ac810ad3ce6db8 Mon Sep 17 00:00:00 2001
From: Willy Tarreau <w...@1wt.eu>
Date: Sun, 20 Mar 2011 10:32:26 +0100
Subject: [PATCH 3/3] [MEDIUM] server: add support for the "send-proxy" option

This option enables use of the PROXY protocol with the server, which
allows haproxy to transport original client's address across multiple
architecture layers.
(cherry picked from commit 5ab04ec47c9946a2bbc535687c023215ca813da0)
---
 doc/configuration.txt  |   15 +++++++++++++++
 include/types/server.h |   39 ++++++++++++++++++++-------------------
 src/backend.c          |    8 ++++++++
 src/cfgparse.c         |    6 +++++-
 4 files changed, 48 insertions(+), 20 deletions(-)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index 36f68a5..0e6a842 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -6116,6 +6116,21 @@ rise <count>
 
   Supported in default-server: Yes
 
+send-proxy
+  The "send-proxy" parameter enforces use of the PROXY protocol over any
+  connection established to this server. The PROXY protocol informs the other
+  end about the layer 3/4 addresses of the incoming connection, so that it can
+  know the client's address or the public address it accessed to, whatever the
+  upper layer protocol. For connections accepted by an "accept-proxy" listener,
+  the advertised address will be used. Only TCPv4 and TCPv6 address families
+  are supported. Other families such as Unix sockets, will report an UNKNOWN
+  family. Servers using this option can fully be chained to another instance of
+  haproxy listening with an "accept-proxy" setting. This setting must not be
+  used if the server isn't aware of the protocol. See also the "accept-proxy"
+  option of the "bind" keyword.
+
+  Supported in default-server: No
+
 slowstart <start_time_in_ms>
   The "slowstart" parameter for a server accepts a value in milliseconds which
   indicates after how long a server which has just come back up will run at
diff --git a/include/types/server.h b/include/types/server.h
index 14e4d1f..4c500be 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -1,23 +1,23 @@
 /*
-  include/types/server.h
-  This file defines everything related to servers.
-
-  Copyright (C) 2000-2009 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/types/server.h
+ * This file defines everything related to servers.
+ *
+ * Copyright (C) 2000-2011 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 _TYPES_SERVER_H
 #define _TYPES_SERVER_H
@@ -53,6 +53,7 @@
 #define SRV_TPROXY_CLI 0x0300  /* bind to the client's IP+port to reach this 
server */
 #define SRV_TPROXY_DYN 0x0400  /* bind to a dynamically computed non-local 
address */
 #define SRV_TPROXY_MASK        0x0700  /* bind to a non-local address to reach 
this server */
+#define SRV_SEND_PROXY 0x0800  /* this server talks the PROXY protocol */
 
 /* function which act on servers need to return various errors */
 #define SRV_STATUS_OK       0   /* everything is OK. */
diff --git a/src/backend.c b/src/backend.c
index a7a7867..0104bb8 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -916,6 +916,14 @@ int connect_server(struct session *s)
        if (!s->req->cons->connect)
                return SN_ERR_INTERNAL;
 
+       /* process the case where the server requires the PROXY protocol to be 
sent */
+       s->req->cons->send_proxy_ofs = 0;
+       if (s->srv->state & SRV_SEND_PROXY) {
+               s->req->cons->send_proxy_ofs = 1; /* must compute size */
+               if (!(s->flags & SN_FRT_ADDR_SET))
+                       get_frt_addr(s);
+       }
+
        assign_tproxy_address(s);
 
        err = s->req->cons->connect(s->req->cons, s->be, s->srv,
diff --git a/src/cfgparse.c b/src/cfgparse.c
index e7e8602..ec89983 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -3628,6 +3628,10 @@ stats_error_parsing:
                                newsrv->state |= SRV_BACKUP;
                                cur_arg ++;
                        }
+                       else if (!defsrv && !strcmp(args[cur_arg], 
"send-proxy")) {
+                               newsrv->state |= SRV_SEND_PROXY;
+                               cur_arg ++;
+                       }
                        else if (!strcmp(args[cur_arg], "weight")) {
                                int w;
                                w = atol(args[cur_arg + 1]);
@@ -3915,7 +3919,7 @@ stats_error_parsing:
                        }
                        else {
                                if (!defsrv)
-                                       Alert("parsing [%s:%d] : server %s only 
supports options 'backup', 'cookie', 'redir', 'observer', 'on-error', 
'error-limit', 'check', 'disabled', 'track', 'id', 'inter', 'fastinter', 
'downinter', 'rise', 'fall', 'addr', 'port', 'source', 'minconn', 'maxconn', 
'maxqueue', 'slowstart' and 'weight'.\n",
+                                       Alert("parsing [%s:%d] : server %s only 
supports options 'backup', 'cookie', 'redir', 'observer', 'on-error', 
'error-limit', 'check', 'disabled', 'track', 'id', 'inter', 'fastinter', 
'downinter', 'rise', 'fall', 'addr', 'port', 'source', 'send-proxy', 'minconn', 
'maxconn', 'maxqueue', 'slowstart' and 'weight'.\n",
                                              file, linenum, newsrv->id);
                                else
                                        Alert("parsing [%s:%d]: default-server 
only supports options 'on-error', 'error-limit', 'inter', 'fastinter', 
'downinter', 'rise', 'fall', 'port', 'minconn', 'maxconn', 'maxqueue', 
'slowstart' and 'weight'.\n",
-- 
1.7.2.1.45.g54fbc

Reply via email to