|
|
- From af2038557a14bf6e2915bed545e216a0f1a95fc5 Mon Sep 17 00:00:00 2001
- From: =?UTF-8?q?Cyril=20Bont=C3=A9?= <cyril.bonte@free.fr>
- Date: Mon, 15 Apr 2013 22:05:00 +0200
- Subject: [PATCH] Proxy Protocol based on haproxy 1.4.23
-
- ---
- doc/configuration.txt | 26 ++++++-
- include/common/standard.h | 25 ++++++-
- include/proto/client.h | 1 +
- include/types/buffers.h | 20 ++---
- include/types/protocols.h | 1 +
- src/cfgparse.c | 15 +++-
- src/client.c | 186 ++++++++++++++++++++++++++++++++++++++++++++++
- src/proto_http.c | 4 +-
- src/session.c | 7 ++
- src/standard.c | 9 ++-
- 10 files changed, 275 insertions(+), 19 deletions(-)
-
- Index: haproxy-1.4.25/doc/configuration.txt
- ===================================================================
- --- haproxy-1.4.25.orig/doc/configuration.txt
- +++ haproxy-1.4.25/doc/configuration.txt
- @@ -1343,6 +1343,7 @@ bind [<address>]:<port_range> [, ...] tr
- bind [<address>]:<port_range> [, ...] id <id>
- bind [<address>]:<port_range> [, ...] name <name>
- bind [<address>]:<port_range> [, ...] defer-accept
- +bind [<address>]:<port_range> [, ...] accept-proxy
- Define one or several listening addresses and/or ports in a frontend.
- May be used in sections : defaults | frontend | listen | backend
- no | yes | yes | no
- @@ -1423,6 +1424,19 @@ bind [<address>]:<port_range> [, ...] de
- with front firewalls which would see an established
- connection while the proxy will only see it in SYN_RECV.
-
- + accept-proxy is an optional keyword which enforces use of the PROXY
- + protocol over any connection accepted by this listener. The
- + PROXY protocol dictates the layer 3/4 addresses of the
- + incoming connection to be used everywhere an address is used,
- + with the only exception of "tcp-request connection" rules
- + which will only see the real connection address. Logs will
- + reflect the addresses indicated in the protocol, unless it is
- + violated, in which case the real address will still be used.
- + This keyword combined with support from external components
- + can be used as an efficient and reliable alternative to the
- + X-Forwarded-For mechanism which is not always reliable and
- + not even always usable.
- +
- It is possible to specify a list of address:port combinations delimited by
- commas. The frontend will then listen on all of these addresses. There is no
- fixed limit to the number of addresses and ports which can be listened on in
- @@ -1433,8 +1447,10 @@ bind [<address>]:<port_range> [, ...] de
- listen http_proxy
- bind :80,:443
- bind 10.0.0.1:10080,10.0.0.1:10443
- + bind 127.0.0.1:8443 accept-proxy
-
- - See also : "source".
- + See also : "source", "option forwardfor" and the PROXY protocol
- + documentation.
-
-
- bind-process [ all | odd | even | <number 1-32> ] ...
- @@ -7257,7 +7273,9 @@ marked with a star ('*') after the field
-
- Detailed fields description :
- - "client_ip" is the IP address of the client which initiated the TCP
- - connection to haproxy.
- + connection to haproxy. Note that when the connection is accepted on a
- + socket configured with "accept-proxy" and the PROXY protocol is correctly
- + used, then the logs will reflect the forwarded connection's information.
-
- - "client_port" is the TCP port of the client which initiated the connection.
-
- @@ -7430,7 +7448,9 @@ with a star ('*') after the field name b
-
- Detailed fields description :
- - "client_ip" is the IP address of the client which initiated the TCP
- - connection to haproxy.
- + connection to haproxy. Note that when the connection is accepted on a
- + socket configured with "accept-proxy" and the PROXY protocol is correctly
- + used, then the logs will reflect the forwarded connection's information.
-
- - "client_port" is the TCP port of the client which initiated the connection.
-
- Index: haproxy-1.4.25/include/common/standard.h
- ===================================================================
- --- haproxy-1.4.25.orig/include/common/standard.h
- +++ haproxy-1.4.25/include/common/standard.h
- @@ -269,6 +269,28 @@ static inline unsigned int __strl2uic(co
- return i;
- }
-
- +/* This function reads an unsigned integer from the string pointed to by <s>
- + * and returns it. The <s> pointer is adjusted to point to the first unread
- + * char. The function automatically stops at <end>.
- + */
- +static inline unsigned int __read_uint(const char **s, const char *end)
- +{
- + const char *ptr = *s;
- + unsigned int i = 0;
- + unsigned int j, k;
- +
- + while (ptr < end) {
- + j = *ptr - '0';
- + k = i * 10;
- + if (j > 9)
- + break;
- + i = k + j;
- + ptr++;
- + }
- + *s = ptr;
- + return i;
- +}
- +
- extern unsigned int str2ui(const char *s);
- extern unsigned int str2uic(const char *s);
- extern unsigned int strl2ui(const char *s, int len);
- @@ -276,9 +298,10 @@ extern unsigned int strl2uic(const char
- extern int strl2ic(const char *s, int len);
- extern int strl2irc(const char *s, int len, int *ret);
- extern int strl2llrc(const char *s, int len, long long *ret);
- +extern unsigned int read_uint(const char **s, const char *end);
- unsigned int inetaddr_host(const char *text);
- unsigned int inetaddr_host_lim(const char *text, const char *stop);
- -unsigned int inetaddr_host_lim_ret(const char *text, char *stop, const char **ret);
- +unsigned int inetaddr_host_lim_ret(char *text, char *stop, char **ret);
-
- static inline char *cut_crlf(char *s) {
-
- Index: haproxy-1.4.25/include/proto/client.h
- ===================================================================
- --- haproxy-1.4.25.orig/include/proto/client.h
- +++ haproxy-1.4.25/include/proto/client.h
- @@ -25,6 +25,7 @@
- #include <common/config.h>
- #include <types/session.h>
-
- +int frontend_decode_proxy_request(struct session *s, struct buffer *req, int an_bit);
- void get_frt_addr(struct session *s);
- int event_accept(int fd);
-
- Index: haproxy-1.4.25/include/types/buffers.h
- ===================================================================
- --- haproxy-1.4.25.orig/include/types/buffers.h
- +++ haproxy-1.4.25/include/types/buffers.h
- @@ -135,16 +135,16 @@
- * The field is blanked by buffer_init() and only by analysers themselves
- * afterwards.
- */
- -#define AN_REQ_INSPECT 0x00000001 /* inspect request contents */
- -#define AN_REQ_WAIT_HTTP 0x00000002 /* wait for an HTTP request */
- -#define AN_REQ_HTTP_PROCESS_FE 0x00000004 /* process the frontend's HTTP part */
- -#define AN_REQ_SWITCHING_RULES 0x00000008 /* apply the switching rules */
- -#define AN_REQ_HTTP_PROCESS_BE 0x00000010 /* process the backend's HTTP part */
- -#define AN_REQ_HTTP_INNER 0x00000020 /* inner processing of HTTP request */
- -#define AN_REQ_HTTP_TARPIT 0x00000040 /* wait for end of HTTP tarpit */
- -#define AN_REQ_HTTP_BODY 0x00000080 /* inspect HTTP request body */
- -#define AN_REQ_STICKING_RULES 0x00000100 /* table persistence matching */
- -/* unused: 0x200 */
- +#define AN_REQ_DECODE_PROXY 0x00000001 /* take the proxied address from a 'PROXY' line */
- +#define AN_REQ_INSPECT 0x00000002 /* inspect request contents */
- +#define AN_REQ_WAIT_HTTP 0x00000004 /* wait for an HTTP request */
- +#define AN_REQ_HTTP_PROCESS_FE 0x00000008 /* process the frontend's HTTP part */
- +#define AN_REQ_SWITCHING_RULES 0x00000010 /* apply the switching rules */
- +#define AN_REQ_HTTP_PROCESS_BE 0x00000020 /* process the backend's HTTP part */
- +#define AN_REQ_HTTP_INNER 0x00000040 /* inner processing of HTTP request */
- +#define AN_REQ_HTTP_TARPIT 0x00000080 /* wait for end of HTTP tarpit */
- +#define AN_REQ_HTTP_BODY 0x00000100 /* inspect HTTP request body */
- +#define AN_REQ_STICKING_RULES 0x00000200 /* table persistence matching */
- #define AN_REQ_PRST_RDP_COOKIE 0x00000400 /* persistence on rdp cookie */
- #define AN_REQ_HTTP_XFER_BODY 0x00000800 /* forward request body */
-
- Index: haproxy-1.4.25/include/types/protocols.h
- ===================================================================
- --- haproxy-1.4.25.orig/include/types/protocols.h
- +++ haproxy-1.4.25/include/types/protocols.h
- @@ -72,6 +72,7 @@
- #define LI_O_FOREIGN 0x0002 /* permit listening on foreing addresses */
- #define LI_O_NOQUICKACK 0x0004 /* disable quick ack of immediate data (linux) */
- #define LI_O_DEF_ACCEPT 0x0008 /* wait up to 1 second for data before accepting */
- +#define LI_O_ACC_PROXY 0x0010 /* find the proxied address in the first request line */
-
- /* The listener will be directly referenced by the fdtab[] which holds its
- * socket. The listener provides the protocol-specific accept() function to
- Index: haproxy-1.4.25/src/cfgparse.c
- ===================================================================
- --- haproxy-1.4.25.orig/src/cfgparse.c
- +++ haproxy-1.4.25/src/cfgparse.c
- @@ -1467,6 +1467,16 @@ int cfg_parse_listen(const char *file, i
- #endif
- }
-
- + if (!strcmp(args[cur_arg], "accept-proxy")) { /* expect a 'PROXY' line first */
- + struct listener *l;
- +
- + for (l = curproxy->listen; l != last_listen; l = l->next)
- + l->options |= LI_O_ACC_PROXY;
- +
- + cur_arg ++;
- + continue;
- + }
- +
- if (!strcmp(args[cur_arg], "name")) {
- struct listener *l;
-
- @@ -1519,7 +1529,7 @@ int cfg_parse_listen(const char *file, i
- continue;
- }
-
- - Alert("parsing [%s:%d] : '%s' only supports the 'transparent', 'defer-accept', 'name', 'id', 'mss' and 'interface' options.\n",
- + Alert("parsing [%s:%d] : '%s' only supports the 'transparent', 'accept-proxy', 'defer-accept', 'name', 'id', 'mss' and 'interface' options.\n",
- file, linenum, args[0]);
- err_code |= ERR_ALERT | ERR_FATAL;
- goto out;
- @@ -5743,6 +5753,9 @@ out_uri_auth_compat:
- listener->handler = process_session;
- listener->analysers |= curproxy->fe_req_ana;
-
- + if (listener->options & LI_O_ACC_PROXY)
- + listener->analysers |= AN_REQ_DECODE_PROXY;
- +
- /* smart accept mode is automatic in HTTP mode */
- if ((curproxy->options2 & PR_O2_SMARTACC) ||
- (curproxy->mode == PR_MODE_HTTP &&
- Index: haproxy-1.4.25/src/client.c
- ===================================================================
- --- haproxy-1.4.25.orig/src/client.c
- +++ haproxy-1.4.25/src/client.c
- @@ -22,6 +22,7 @@
-
- #include <common/compat.h>
- #include <common/config.h>
- +#include <common/debug.h>
- #include <common/time.h>
-
- #include <types/global.h>
- @@ -43,6 +44,191 @@
- #include <proto/task.h>
-
-
- +/* This analyser tries to fetch a line from the request buffer which looks like :
- + *
- + * "PROXY" <SP> PROTO <SP> SRC3 <SP> DST3 <SP> SRC4 <SP> <DST4> "\r\n"
- + *
- + * There must be exactly one space between each field. Fields are :
- + * - PROTO : layer 4 protocol, which must be "TCP4" or "TCP6".
- + * - SRC3 : layer 3 (eg: IP) source address in standard text form
- + * - DST3 : layer 3 (eg: IP) destination address in standard text form
- + * - SRC4 : layer 4 (eg: TCP port) source address in standard text form
- + * - DST4 : layer 4 (eg: TCP port) destination address in standard text form
- + *
- + * This line MUST be at the beginning of the buffer and MUST NOT wrap.
- + *
- + * Once the data is fetched, the values are set in the session's field and data
- + * are removed from the buffer. The function returns zero if it needs to wait
- + * for more data (max: timeout_client), or 1 if it has finished and removed itself.
- + */
- +int frontend_decode_proxy_request(struct session *s, struct buffer *req, int an_bit)
- +{
- + char *line = req->data;
- + char *end = req->data + req->l;
- + int len;
- +
- + DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
- + now_ms, __FUNCTION__,
- + s,
- + req,
- + req->rex, req->wex,
- + req->flags,
- + req->l,
- + req->analysers);
- +
- + if (req->flags & (BF_READ_ERROR|BF_READ_TIMEOUT))
- + goto fail;
- +
- + len = MIN(req->l, 6);
- + if (!len)
- + goto missing;
- +
- + /* Decode a possible proxy request, fail early if it does not match */
- + if (strncmp(line, "PROXY ", len) != 0)
- + goto fail;
- +
- + line += 6;
- + if (req->l < 18) /* shortest possible line */
- + goto missing;
- +
- + if (!memcmp(line, "TCP4 ", 5) != 0) {
- + u32 src3, dst3, sport, dport;
- +
- + line += 5;
- +
- + src3 = inetaddr_host_lim_ret(line, end, &line);
- + if (line == end)
- + goto missing;
- + if (*line++ != ' ')
- + goto fail;
- +
- + dst3 = inetaddr_host_lim_ret(line, end, &line);
- + if (line == end)
- + goto missing;
- + if (*line++ != ' ')
- + goto fail;
- +
- + sport = read_uint((const char **)&line, end);
- + if (line == end)
- + goto missing;
- + if (*line++ != ' ')
- + goto fail;
- +
- + dport = read_uint((const char **)&line, end);
- + if (line > end - 2)
- + goto missing;
- + if (*line++ != '\r')
- + goto fail;
- + if (*line++ != '\n')
- + goto fail;
- +
- + /* update the session's addresses and mark them set */
- + ((struct sockaddr_in *)&s->cli_addr)->sin_family = AF_INET;
- + ((struct sockaddr_in *)&s->cli_addr)->sin_addr.s_addr = htonl(src3);
- + ((struct sockaddr_in *)&s->cli_addr)->sin_port = htons(sport);
- +
- + ((struct sockaddr_in *)&s->frt_addr)->sin_family = AF_INET;
- + ((struct sockaddr_in *)&s->frt_addr)->sin_addr.s_addr = htonl(dst3);
- + ((struct sockaddr_in *)&s->frt_addr)->sin_port = htons(dport);
- + s->flags |= SN_FRT_ADDR_SET;
- +
- + }
- + else if (!memcmp(line, "TCP6 ", 5) != 0) {
- + u32 sport, dport;
- + char *src_s;
- + char *dst_s, *sport_s, *dport_s;
- + struct in6_addr src3, dst3;
- +
- + line+=5;
- +
- + src_s = line;
- + dst_s = sport_s = dport_s = NULL;
- + while (1) {
- + if (line > end - 2) {
- + goto missing;
- + }
- + else if (*line == '\r') {
- + *line = 0;
- + line++;
- + if (*line++ != '\n')
- + goto fail;
- + break;
- + }
- +
- + if (*line == ' ') {
- + *line = 0;
- + if (!dst_s)
- + dst_s = line+1;
- + else if (!sport_s)
- + sport_s = line+1;
- + else if (!dport_s)
- + dport_s = line+1;
- + }
- + line++;
- + }
- +
- + if (!dst_s || !sport_s || !dport_s)
- + goto fail;
- +
- + sport = read_uint((const char **)&sport_s,dport_s-1);
- + if ( *sport_s != 0 )
- + goto fail;
- +
- + dport = read_uint((const char **)&dport_s,line-2);
- + if ( *dport_s != 0 )
- + goto fail;
- +
- + if (inet_pton(AF_INET6, src_s, (void *)&src3) != 1)
- + goto fail;
- +
- + if (inet_pton(AF_INET6, dst_s, (void *)&dst3) != 1)
- + goto fail;
- +
- + /* update the session's addresses and mark them set */
- + ((struct sockaddr_in6 *)&s->cli_addr)->sin6_family = AF_INET6;
- + memcpy(&((struct sockaddr_in6 *)&s->cli_addr)->sin6_addr, &src3, sizeof(struct in6_addr));
- + ((struct sockaddr_in6 *)&s->cli_addr)->sin6_port = htons(sport);
- +
- + ((struct sockaddr_in6 *)&s->frt_addr)->sin6_family = AF_INET6;
- + memcpy(&((struct sockaddr_in6 *)&s->frt_addr)->sin6_addr, &dst3, sizeof(struct in6_addr));
- + ((struct sockaddr_in6 *)&s->frt_addr)->sin6_port = htons(dport);
- + s->flags |= SN_FRT_ADDR_SET;
- + }
- + else {
- + goto fail;
- + }
- +
- + /* remove the PROXY line from the request */
- + len = line - req->data;
- + buffer_replace2(req, req->data, line, NULL, 0);
- + req->total -= len; /* don't count the header line */
- +
- + req->analysers &= ~an_bit;
- + return 1;
- +
- + missing:
- + if (!(req->flags & (BF_SHUTR|BF_FULL))) {
- + buffer_dont_connect(s->req);
- + return 0;
- + }
- + /* missing data and buffer is either full or shutdown => fail */
- +
- + fail:
- + buffer_abort(req);
- + buffer_abort(s->rep);
- + req->analysers = 0;
- +
- + s->fe->counters.failed_req++;
- + if (s->listener->counters)
- + s->listener->counters->failed_req++;
- +
- + if (!(s->flags & SN_ERR_MASK))
- + s->flags |= SN_ERR_PRXCOND;
- + if (!(s->flags & SN_FINST_MASK))
- + s->flags |= SN_FINST_R;
- + return 0;
- +}
- +
- /* Retrieves the original destination address used by the client, and sets the
- * SN_FRT_ADDR_SET flag.
- */
- Index: haproxy-1.4.25/src/proto_http.c
- ===================================================================
- --- haproxy-1.4.25.orig/src/proto_http.c
- +++ haproxy-1.4.25/src/proto_http.c
- @@ -4209,7 +4209,8 @@ void http_end_txn_clean_session(struct s
- if (s->rep->lr >= s->rep->data + s->rep->size)
- s->rep->lr -= s->req->size;
-
- - s->req->analysers |= s->fe->fe_req_ana;
- + s->req->analysers = s->fe->fe_req_ana;
- + s->req->analysers &= ~AN_REQ_DECODE_PROXY;
- s->rep->analysers = 0;
-
- http_silent_debug(__LINE__, s);
- @@ -7807,7 +7808,6 @@ void http_reset_txn(struct session *s)
- http_init_txn(s);
-
- s->be = s->fe;
- - s->req->analysers = s->listener->analysers;
- s->logs.logwait = s->fe->to_log;
- s->srv = s->prev_srv = s->srv_conn = NULL;
- /* re-init store persistence */
- Index: haproxy-1.4.25/src/session.c
- ===================================================================
- --- haproxy-1.4.25.orig/src/session.c
- +++ haproxy-1.4.25/src/session.c
- @@ -34,6 +34,7 @@
- #include <proto/proxy.h>
- #include <proto/queue.h>
- #include <proto/server.h>
- +#include <proto/client.h>
- #include <proto/stick_table.h>
- #include <proto/stream_interface.h>
- #include <proto/stream_sock.h>
- @@ -1083,6 +1084,12 @@ resync_stream_interface:
- while (ana_list && max_loops--) {
- /* Warning! ensure that analysers are always placed in ascending order! */
-
- + if (ana_list & AN_REQ_DECODE_PROXY) {
- + if (!frontend_decode_proxy_request(s, s->req, AN_REQ_DECODE_PROXY))
- + break;
- + UPDATE_ANALYSERS(s->req->analysers, ana_list, ana_back, AN_REQ_DECODE_PROXY);
- + }
- +
- if (ana_list & AN_REQ_INSPECT) {
- if (!tcp_inspect_request(s, s->req, AN_REQ_INSPECT))
- break;
- Index: haproxy-1.4.25/src/standard.c
- ===================================================================
- --- haproxy-1.4.25.orig/src/standard.c
- +++ haproxy-1.4.25/src/standard.c
- @@ -569,6 +569,11 @@ unsigned int strl2uic(const char *s, int
- return __strl2uic(s, len);
- }
-
- +unsigned int read_uint(const char **s, const char *end)
- +{
- + return __read_uint(s, end);
- +}
- +
- /* This one is 7 times faster than strtol() on athlon with checks.
- * It returns the value of the number composed of all valid digits read,
- * and can process negative numbers too.
- @@ -993,12 +998,12 @@ unsigned int inetaddr_host_lim(const cha
- * Idem except the pointer to first unparsed byte is returned into <ret> which
- * must not be NULL.
- */
- -unsigned int inetaddr_host_lim_ret(const char *text, char *stop, const char **ret)
- +unsigned int inetaddr_host_lim_ret(char *text, char *stop, char **ret)
- {
- const unsigned int ascii_zero = ('0' << 24) | ('0' << 16) | ('0' << 8) | '0';
- register unsigned int dig100, dig10, dig1;
- int s;
- - const char *p, *d;
- + char *p, *d;
-
- dig1 = dig10 = dig100 = ascii_zero;
- s = 24;
|