Hi

Here's a set of patches for PROXY support in Varnish master, as
discussed at the meeting in Barcelona last November
(https://www.varnish-cache.org/trac/wiki/VDD14Q4#Notestakenduringthemeeting).

0001-Introduce-local.ip-and-remote.ip.patch
0002-Make-client.ip-settable-from-VCL.patch

These patches introduce new VCL vars local.ip and remote.ip, and makes
client.ip writable from VCL. local.ip and remote.ip are immutable and
will always represent the endpoints of the local socket.

VCL will then have four different IP vars (client.ip, server.ip,
remote.ip, local.ip). By default (no PROXY proto), client.ip and
server.ip will be set to the values of remote.ip and client.ip
respectively. With PROXY protocol, they will get their values set from
the PROXY header.

0003-Add-support-for-specifying-protocol-to-the-listen-so.patch:
This adds an optional protocol specifier to the -a command line
argument, to let us specify e.g. "-a [email protected]:8081" to enable
PROXY protocol for that listen endpoint.

0004-Set-blocking-mode-prior-to-entering-HTTP-1-FSM.patch:
PROXY code needed blocking mode, so this had to be reorganized slightly.

0005-Preliminary-PROXY-v1-v2-support.patch:
This is the big one that adds handling and parsing of PROXY v1 and v2.
Adds a new fsm step S_STP_PROXY where we end up when a sess comes in
from the acceptor on a PROXY protocol listen endpoint.

If the sender does not satisfy the exact specifications of the PROXY
protocol, we close the connection. With a valid PROXY header, the
addresses will be available in client.ip and server.ip in VCL.

0006-Update-graphviz-doc-to-reflect-new-S_STP_PROXY-step.patch:
And finally update the dotty diagrams to reflect how the state machine
looks with PROXY protocol support.

Further work:
- Byte counters for PROXY protocol bytes. Is req_hdrbytes an
acceptable place to stick it?
- Put an example like this somewhere in the docs, to help users be
safe from a spoofing attack:

acl trusted_remotes {
  // ...
}

sub vcl_recv {
  if (remote.ip !~ trusted_remotes) {
    return (synth(403, "Forbidden"));
  }
}


Comments very much appreciated.

-Dag

-- 
Dag Haavi Finstad
Software Developer | Varnish Software
Mobile: +47 476 64 134
We Make Websites Fly!
From 0d4a98f744480f4571a6f1bb37c2ef6fa30e6866 Mon Sep 17 00:00:00 2001
From: Dag Haavi Finstad <[email protected]>
Date: Fri, 9 Jan 2015 11:37:20 +0100
Subject: [PATCH 1/6] Introduce local.ip and remote.ip.

---
 bin/varnishd/cache/cache.h         |  8 ++++----
 bin/varnishd/cache/cache_panic.c   |  9 +++++++--
 bin/varnishd/cache/cache_req_fsm.c | 19 +++++++++++++++----
 bin/varnishd/cache/cache_session.c | 17 ++++++++---------
 bin/varnishd/cache/cache_vrt_var.c | 26 +++++++++++++++++++++++---
 include/tbl/vsl_tags.h             | 27 +++++++++++++++------------
 lib/libvcc/generate.py             | 19 +++++++++++++++++--
 7 files changed, 89 insertions(+), 36 deletions(-)

diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h
index fccc466..d78ff5f 100644
--- a/bin/varnishd/cache/cache.h
+++ b/bin/varnishd/cache/cache.h
@@ -682,10 +682,10 @@ struct sess {
 	((struct suckaddr *)(void*)((sp)->addrs))
 #define sess_local_addr(sp) \
 	((struct suckaddr *)(void*)((sp)->addrs + vsa_suckaddr_len))
-
-	/* formatted ascii client address */
-	char			*client_addr_str;
-	char			*client_port_str;
+#define sess_client_addr(sp) \
+	((struct suckaddr *)(void*)((sp)->addrs + vsa_suckaddr_len * 2))
+#define sess_server_addr(sp) \
+	((struct suckaddr *)(void*)((sp)->addrs + vsa_suckaddr_len * 3))
 
 	/* Timestamps, all on TIM_real() timescale */
 	double			t_open;		/* fd accepted */
diff --git a/bin/varnishd/cache/cache_panic.c b/bin/varnishd/cache/cache_panic.c
index a5c2ce6..e7b3c3d 100644
--- a/bin/varnishd/cache/cache_panic.c
+++ b/bin/varnishd/cache/cache_panic.c
@@ -47,6 +47,7 @@
 #include "cache_backend.h"
 #include "storage/storage.h"
 #include "vcl.h"
+#include "vtcp.h"
 #include "waiter/waiter.h"
 
 /*
@@ -430,12 +431,16 @@ static void
 pan_sess(const struct sess *sp)
 {
 	const char *stp;
+	char raddr[ADDR_BUFSIZE];
+	char rport[PORT_BUFSIZE];
+
+	VTCP_name(sess_remote_addr(sp), raddr, sizeof raddr,
+	    rport, sizeof rport);
 
 	VSB_printf(pan_vsp, "  sp = %p {\n", sp);
 	VSB_printf(pan_vsp, "    fd = %d, vxid = %u,\n",
 	    sp->fd, VXID(sp->vxid));
-	VSB_printf(pan_vsp, "    client = %s %s,\n", sp->client_addr_str,
-		sp->client_port_str);
+	VSB_printf(pan_vsp, "    client = %s %s,\n", raddr, rport);
 	switch (sp->sess_step) {
 #define SESS_STEP(l, u) case S_STP_##u: stp = "S_STP_" #u; break;
 #include "tbl/steps.h"
diff --git a/bin/varnishd/cache/cache_req_fsm.c b/bin/varnishd/cache/cache_req_fsm.c
index e080eee..f7a5fee 100644
--- a/bin/varnishd/cache/cache_req_fsm.c
+++ b/bin/varnishd/cache/cache_req_fsm.c
@@ -48,6 +48,8 @@
 #include "hash/hash_slinger.h"
 #include "vcl.h"
 #include "vsha256.h"
+#include "vsa.h"
+#include "vtcp.h"
 #include "vtim.h"
 
 /*--------------------------------------------------------------------
@@ -565,10 +567,15 @@ cnt_recv(struct worker *wrk, struct req *req)
 {
 	unsigned recv_handling;
 	struct SHA256Context sha256ctx;
+	char raddr[ADDR_BUFSIZE];
+	char rport[PORT_BUFSIZE];
+	char caddr[ADDR_BUFSIZE];
+	char cport[PORT_BUFSIZE];
 	const char *xff;
 
 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
 	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+	CHECK_OBJ_NOTNULL(req->sp, SESS_MAGIC);
 	CHECK_OBJ_NOTNULL(req->vcl, VCL_CONF_MAGIC);
 	AZ(req->objcore);
 	AZ(req->err_code);
@@ -577,8 +584,12 @@ cnt_recv(struct worker *wrk, struct req *req)
 	AZ(isnan(req->t_prev));
 	AZ(isnan(req->t_req));
 
-	VSLb(req->vsl, SLT_ReqStart, "%s %s",
-	    req->sp->client_addr_str, req->sp->client_port_str);
+	VTCP_name(sess_remote_addr(req->sp), raddr, sizeof raddr, rport,
+	    sizeof rport);
+	VTCP_name(sess_client_addr(req->sp), caddr, sizeof caddr, cport,
+	    sizeof cport);
+
+	VSLb(req->vsl, SLT_ReqStart, "%s %s %s %s", caddr, cport, raddr, rport);
 
 	http_VSL_log(req->http);
 
@@ -591,10 +602,10 @@ cnt_recv(struct worker *wrk, struct req *req)
 		if (http_GetHdr(req->http, H_X_Forwarded_For, &xff)) {
 			http_Unset(req->http, H_X_Forwarded_For);
 			http_PrintfHeader(req->http, "X-Forwarded-For: %s, %s",
-			    xff, req->sp->client_addr_str);
+			    xff, caddr);
 		} else {
 			http_PrintfHeader(req->http, "X-Forwarded-For: %s",
-			    req->sp->client_addr_str);
+			    caddr);
 		}
 	}
 
diff --git a/bin/varnishd/cache/cache_session.c b/bin/varnishd/cache/cache_session.c
index 7992834..bc11a85 100644
--- a/bin/varnishd/cache/cache_session.c
+++ b/bin/varnishd/cache/cache_session.c
@@ -88,7 +88,7 @@ ses_new(struct sesspool *pp)
 	p = (void*)PRNDUP(p);
 	assert(p < e);
 	WS_Init(sp->ws, "ses", p, e - p);
-	sp->addrs = WS_Alloc(sp->ws, vsa_suckaddr_len * 2);
+	sp->addrs = WS_Alloc(sp->ws, vsa_suckaddr_len * 4);
 	AN(sp->addrs);
 
 	sp->t_open = NAN;
@@ -157,6 +157,8 @@ ses_vsl_socket(struct sess *sp, const char *lsockname)
 {
 	struct sockaddr_storage ss;
 	socklen_t sl;
+	char raddr[ADDR_BUFSIZE];
+	char rport[PORT_BUFSIZE];
 	char laddr[ADDR_BUFSIZE];
 	char lport[PORT_BUFSIZE];
 
@@ -169,18 +171,15 @@ ses_vsl_socket(struct sess *sp, const char *lsockname)
 	AN(VSA_Build(sess_local_addr(sp), &ss, sl));
 	assert(VSA_Sane(sess_local_addr(sp)));
 
-	VTCP_name(sess_remote_addr(sp), laddr, sizeof laddr,
-	    lport, sizeof lport);
-	sp->client_addr_str = WS_Copy(sp->ws, laddr, -1);
-	AN(sp->client_addr_str);
-	sp->client_port_str = WS_Copy(sp->ws, lport, -1);
-	AN(sp->client_port_str);
+	VTCP_name(sess_remote_addr(sp), raddr, sizeof raddr,
+	    rport, sizeof rport);
 	VTCP_name(sess_local_addr(sp), laddr, sizeof laddr,
 	    lport, sizeof lport);
 	VSL(SLT_Begin, sp->vxid, "sess 0 HTTP/1");
 	VSL(SLT_SessOpen, sp->vxid, "%s %s %s %s %s %.6f %d",
-	    sp->client_addr_str, sp->client_port_str, lsockname, laddr, lport,
-	    sp->t_open, sp->fd);
+	    raddr, rport, lsockname, laddr, lport, sp->t_open, sp->fd);
+	memcpy(sess_client_addr(sp), sess_remote_addr(sp),
+	    vsa_suckaddr_len * 2);
 }
 
 /*--------------------------------------------------------------------
diff --git a/bin/varnishd/cache/cache_vrt_var.c b/bin/varnishd/cache/cache_vrt_var.c
index fdfe1de..d86f855 100644
--- a/bin/varnishd/cache/cache_vrt_var.c
+++ b/bin/varnishd/cache/cache_vrt_var.c
@@ -242,10 +242,11 @@ VRT_r_client_identity(VRT_CTX)
 
 	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
 	CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC);
+	CHECK_OBJ_NOTNULL(ctx->req->sp, SESS_MAGIC);
 	if (ctx->req->client_identity != NULL)
 		return (ctx->req->client_identity);
 	else
-		return (ctx->req->sp->client_addr_str);
+		return (VRT_IP_string(ctx, sess_client_addr(ctx->req->sp)));
 }
 
 void
@@ -603,7 +604,7 @@ VRT_r_req_##field(VRT_CTX)				\
 /*--------------------------------------------------------------------*/
 
 VCL_IP
-VRT_r_client_ip(VRT_CTX)
+VRT_r_remote_ip(VRT_CTX)
 {
 
 	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
@@ -613,7 +614,7 @@ VRT_r_client_ip(VRT_CTX)
 }
 
 VCL_IP
-VRT_r_server_ip(VRT_CTX)
+VRT_r_local_ip(VRT_CTX)
 {
 
 	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
@@ -622,6 +623,25 @@ VRT_r_server_ip(VRT_CTX)
 	return (sess_local_addr(ctx->req->sp));
 }
 
+VCL_IP
+VRT_r_client_ip(VRT_CTX)
+{
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+	CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC);
+	CHECK_OBJ_NOTNULL(ctx->req->sp, SESS_MAGIC);
+	return (sess_client_addr(ctx->req->sp));
+}
+
+VCL_IP
+VRT_r_server_ip(VRT_CTX)
+{
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+	CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC);
+	CHECK_OBJ_NOTNULL(ctx->req->sp, SESS_MAGIC);
+	return (sess_server_addr(ctx->req->sp));
+}
+
+
 const char*
 VRT_r_server_identity(VRT_CTX)
 {
diff --git a/include/tbl/vsl_tags.h b/include/tbl/vsl_tags.h
index 7dd918d..a8a79e4 100644
--- a/include/tbl/vsl_tags.h
+++ b/include/tbl/vsl_tags.h
@@ -65,14 +65,15 @@ SLTM(SessOpen, 0, "Client connection opened",
 	"The first record for a client connection, with the socket-endpoints"
 	" of the connection.\n\n"
 	"The format is::\n\n"
-	"\t%s %d %s %s %s %d\n"
-	"\t|  |  |  |  |  |\n"
-	"\t|  |  |  |  |  +- File descriptor number\n"
-	"\t|  |  |  |  +---- Local TCP port ('-' if !$log_local_addr)\n"
-	"\t|  |  |  +------- Local IPv4/6 address ('-' if !$log_local_addr)\n"
-	"\t|  |  +---------- Listen socket\n"
-	"\t|  +------------- Client TCP socket\n"
-	"\t+---------------- Client IPv4/6 address\n"
+	"\t%s %d %s %s %s %.6f %d\n"
+	"\t|  |  |  |  |  |    |\n"
+	"\t|  |  |  |  |  |    +- File descriptor number\n"
+	"\t|  |  |  |  |  +------ Timestamp\n"
+	"\t|  |  |  |  +--------- Local TCP port\n"
+	"\t|  |  |  +------------ Local IPv4/6 address\n"
+	"\t|  |  +--------------- Listen socket\n"
+	"\t|  +------------------ Remote TCP port\n"
+	"\t+--------------------- Remote IPv4/6 address\n"
 	"\n"
 )
 
@@ -255,10 +256,12 @@ SLTM(ReqStart, 0, "Client request start",
 	"Start of request processing. Logs the client IP address and port"
 	" number.\n\n"
 	"The format is::\n\n"
-	"\t%s %s\n"
-	"\t|  |\n"
-	"\t|  +- Port number\n"
-	"\t+---- IP address\n"
+	"\t%s %s %s %s\n"
+	"\t|  |  |  |\n"
+	"\t|  |  |  +- Remote port number\n"
+	"\t|  |  +---- Remote IP address\n"
+	"\t|  +------- Client port number\n"
+	"\t+---------- Client IP address\n"
 	"\n"
 )
 
diff --git a/lib/libvcc/generate.py b/lib/libvcc/generate.py
index aafcfc0..e545251 100755
--- a/lib/libvcc/generate.py
+++ b/lib/libvcc/generate.py
@@ -176,8 +176,7 @@ sp_variables = [
 		'IP',
 		( 'client',),
 		( ), """
-		The IP address of the socket on which the client
-		connection was received.
+		The IP address of the server the client connected to.
 		"""
 	),
 	('server.hostname',
@@ -197,6 +196,22 @@ sp_variables = [
 		specified by the -n parameter.
 		"""
 	),
+	('remote.ip',
+		'IP',
+		( 'client',),
+		( ), """
+		The remote IP address of the socket on which the client
+		connection was received.
+		"""
+	),
+	('local.ip',
+		'IP',
+		( 'client',),
+		( ), """
+		The IP address of the socket on which the client
+		connection was received.
+		"""
+	),
 	('req',
 		'HTTP',
 		( 'client',),
-- 
2.1.4

From 7090fb0ed30db015d58e0860e675b34c91ff16bc Mon Sep 17 00:00:00 2001
From: Dag Haavi Finstad <[email protected]>
Date: Fri, 9 Jan 2015 13:48:44 +0100
Subject: [PATCH 2/6] Make client.ip settable from VCL.

Also bump workspace_session up to 512 bytes, as 384 proved a little too
small to store all of the addresses.
---
 bin/varnishd/cache/cache.h           |  4 ++-
 bin/varnishd/cache/cache_session.c   |  6 +++--
 bin/varnishd/cache/cache_vrt_var.c   | 11 +++++++++
 bin/varnishd/http1/cache_http1_fsm.c |  4 +++
 bin/varnishd/mgt/mgt_param_tbl.c     |  2 +-
 bin/varnishtest/tests/v00043.vtc     | 47 ++++++++++++++++++++++++++++++++++++
 lib/libvcc/generate.py               |  2 +-
 7 files changed, 71 insertions(+), 5 deletions(-)
 create mode 100644 bin/varnishtest/tests/v00043.vtc

diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h
index d78ff5f..43798e7 100644
--- a/bin/varnishd/cache/cache.h
+++ b/bin/varnishd/cache/cache.h
@@ -682,10 +682,12 @@ struct sess {
 	((struct suckaddr *)(void*)((sp)->addrs))
 #define sess_local_addr(sp) \
 	((struct suckaddr *)(void*)((sp)->addrs + vsa_suckaddr_len))
-#define sess_client_addr(sp) \
+#define sess_client_addr0(sp) \
 	((struct suckaddr *)(void*)((sp)->addrs + vsa_suckaddr_len * 2))
 #define sess_server_addr(sp) \
 	((struct suckaddr *)(void*)((sp)->addrs + vsa_suckaddr_len * 3))
+#define sess_client_addr(sp) \
+	((struct suckaddr *)(void*)((sp)->addrs + vsa_suckaddr_len * 4))
 
 	/* Timestamps, all on TIM_real() timescale */
 	double			t_open;		/* fd accepted */
diff --git a/bin/varnishd/cache/cache_session.c b/bin/varnishd/cache/cache_session.c
index bc11a85..f390cb8 100644
--- a/bin/varnishd/cache/cache_session.c
+++ b/bin/varnishd/cache/cache_session.c
@@ -88,7 +88,7 @@ ses_new(struct sesspool *pp)
 	p = (void*)PRNDUP(p);
 	assert(p < e);
 	WS_Init(sp->ws, "ses", p, e - p);
-	sp->addrs = WS_Alloc(sp->ws, vsa_suckaddr_len * 4);
+	sp->addrs = WS_Alloc(sp->ws, vsa_suckaddr_len * 5);
 	AN(sp->addrs);
 
 	sp->t_open = NAN;
@@ -178,8 +178,10 @@ ses_vsl_socket(struct sess *sp, const char *lsockname)
 	VSL(SLT_Begin, sp->vxid, "sess 0 HTTP/1");
 	VSL(SLT_SessOpen, sp->vxid, "%s %s %s %s %s %.6f %d",
 	    raddr, rport, lsockname, laddr, lport, sp->t_open, sp->fd);
-	memcpy(sess_client_addr(sp), sess_remote_addr(sp),
+	memcpy(sess_client_addr0(sp), sess_remote_addr(sp),
 	    vsa_suckaddr_len * 2);
+	memcpy(sess_client_addr(sp), sess_client_addr0(sp),
+	    vsa_suckaddr_len);
 }
 
 /*--------------------------------------------------------------------
diff --git a/bin/varnishd/cache/cache_vrt_var.c b/bin/varnishd/cache/cache_vrt_var.c
index d86f855..3552428 100644
--- a/bin/varnishd/cache/cache_vrt_var.c
+++ b/bin/varnishd/cache/cache_vrt_var.c
@@ -632,6 +632,17 @@ VRT_r_client_ip(VRT_CTX)
 	return (sess_client_addr(ctx->req->sp));
 }
 
+void
+VRT_l_client_ip(VRT_CTX, VCL_IP ip)
+{
+	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+	CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC);
+	CHECK_OBJ_NOTNULL(ctx->req->sp, SESS_MAGIC);
+	assert(VSA_Sane(ip));
+	if (ip != sess_client_addr(ctx->req->sp))
+		memcpy(sess_client_addr(ctx->req->sp), ip, vsa_suckaddr_len);
+}
+
 VCL_IP
 VRT_r_server_ip(VRT_CTX)
 {
diff --git a/bin/varnishd/http1/cache_http1_fsm.c b/bin/varnishd/http1/cache_http1_fsm.c
index a8c9959..62edd4b 100644
--- a/bin/varnishd/http1/cache_http1_fsm.c
+++ b/bin/varnishd/http1/cache_http1_fsm.c
@@ -41,6 +41,7 @@
 #include "cache/cache.h"
 #include "hash/hash_slinger.h"
 
+#include "vsa.h"
 #include "vcl.h"
 #include "vtcp.h"
 #include "vtim.h"
@@ -177,6 +178,9 @@ http1_cleanup(struct sess *sp, struct worker *wrk, struct req *req)
 
 	VSL_End(req->vsl);
 
+	memcpy(sess_client_addr(sp), sess_client_addr0(sp),
+	    vsa_suckaddr_len);
+
 	if (!isnan(req->t_prev) && req->t_prev > 0.)
 		sp->t_idle = req->t_prev;
 	else
diff --git a/bin/varnishd/mgt/mgt_param_tbl.c b/bin/varnishd/mgt/mgt_param_tbl.c
index fc9dc78..142cad1 100644
--- a/bin/varnishd/mgt/mgt_param_tbl.c
+++ b/bin/varnishd/mgt/mgt_param_tbl.c
@@ -89,7 +89,7 @@ struct parspec mgt_parspec[] = {
 		"Bytes of workspace for session and TCP connection addresses."
 		"  If larger than 4k, use a multiple of 4k for VM efficiency.",
 		DELAYED_EFFECT,
-		"384", "bytes" },
+		"512", "bytes" },
 	{ "workspace_client",
 		tweak_bytes_u, &mgt_param.workspace_client,
 		"9k", NULL,
diff --git a/bin/varnishtest/tests/v00043.vtc b/bin/varnishtest/tests/v00043.vtc
new file mode 100644
index 0000000..425e29a
--- /dev/null
+++ b/bin/varnishtest/tests/v00043.vtc
@@ -0,0 +1,47 @@
+varnishtest "Test assignment to client.ip"
+
+server s1 {
+	rxreq
+	txresp
+} -start
+
+varnish v1 -vcl+backend {
+	import ${vmod_std};
+	sub vcl_deliver {
+		set resp.http.ip0 = client.ip;
+
+		set client.ip = local.ip;
+		set resp.http.ip1 = client.ip;
+
+		if (req.http.ip) {
+			set client.ip = std.ip(req.http.ip, client.ip);
+		}
+		set resp.http.ip2 = client.ip;
+
+		set client.ip = client.ip;
+		set resp.http.ip3 = client.ip;
+	}
+} -start
+
+client c1 {
+	txreq -hdr "ip: 1.2.3.4"
+	rxresp
+	expect resp.http.ip0 == "127.0.0.1"
+	expect resp.http.ip1 == "127.0.0.1"
+	expect resp.http.ip2 == "1.2.3.4"
+	expect resp.http.ip3 == "1.2.3.4"
+
+	txreq -hdr "ip: 2.3.4.5"
+	rxresp
+	expect resp.http.ip0 == "127.0.0.1"
+	expect resp.http.ip1 == "127.0.0.1"
+	expect resp.http.ip2 == "2.3.4.5"
+	expect resp.http.ip3 == "2.3.4.5"
+
+	txreq
+	rxresp
+	expect resp.http.ip0 == "127.0.0.1"
+	expect resp.http.ip1 == "127.0.0.1"
+	expect resp.http.ip2 == "127.0.0.1"
+	expect resp.http.ip3 == "127.0.0.1"
+} -run
diff --git a/lib/libvcc/generate.py b/lib/libvcc/generate.py
index e545251..8e55658 100755
--- a/lib/libvcc/generate.py
+++ b/lib/libvcc/generate.py
@@ -160,7 +160,7 @@ sp_variables = [
 	('client.ip',
 		'IP',
 		( 'client',),
-		( ), """
+		( 'client',), """
 		The client's IP address.
 		"""
 	),
-- 
2.1.4

From 7bd1296c975509e38a8443e2f0602efcdde5ff6f Mon Sep 17 00:00:00 2001
From: Dag Haavi Finstad <[email protected]>
Date: Fri, 9 Jan 2015 15:10:32 +0100
Subject: [PATCH 3/6] Add support for specifying protocol@ to the listen socket
 -a command line argument.

---
 bin/varnishd/cache/cache.h          |  1 +
 bin/varnishd/cache/cache_acceptor.c |  1 +
 bin/varnishd/cache/cache_session.c  | 15 ++++++++++++++-
 bin/varnishd/common/common.h        |  8 ++++++++
 bin/varnishd/common/heritage.h      |  1 +
 bin/varnishd/mgt/mgt_param_tweak.c  | 32 +++++++++++++++++++++++++++++++-
 doc/sphinx/reference/varnishd.rst   |  6 ++++--
 include/tbl/listen_proto.h          | 32 ++++++++++++++++++++++++++++++++
 8 files changed, 92 insertions(+), 4 deletions(-)
 create mode 100644 include/tbl/listen_proto.h

diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h
index 43798e7..9b8ddd3 100644
--- a/bin/varnishd/cache/cache.h
+++ b/bin/varnishd/cache/cache.h
@@ -659,6 +659,7 @@ struct sess {
 	enum sess_step		sess_step;
 	struct lock		mtx;
 	int			fd;
+	unsigned		proto;
 	enum sess_close		reason;
 	uint32_t		vxid;
 
diff --git a/bin/varnishd/cache/cache_acceptor.c b/bin/varnishd/cache/cache_acceptor.c
index 3dc72ca..ab56dfa 100644
--- a/bin/varnishd/cache/cache_acceptor.c
+++ b/bin/varnishd/cache/cache_acceptor.c
@@ -352,6 +352,7 @@ VCA_SetupSess(struct worker *wrk, struct sess *sp)
 	sp->fd = wa->acceptsock;
 	wa->acceptsock = -1;
 	retval = wa->acceptlsock->name;
+	sp->proto = wa->acceptlsock->proto;
 	assert(wa->acceptaddrlen <= vsa_suckaddr_len);
 	AN(VSA_Build(sess_remote_addr(sp), &wa->acceptaddr, wa->acceptaddrlen));
 	vca_pace_good();
diff --git a/bin/varnishd/cache/cache_session.c b/bin/varnishd/cache/cache_session.c
index f390cb8..f318f1e 100644
--- a/bin/varnishd/cache/cache_session.c
+++ b/bin/varnishd/cache/cache_session.c
@@ -161,6 +161,9 @@ ses_vsl_socket(struct sess *sp, const char *lsockname)
 	char rport[PORT_BUFSIZE];
 	char laddr[ADDR_BUFSIZE];
 	char lport[PORT_BUFSIZE];
+#define PROTOSTR_MAX 64
+	char pbuf[PROTOSTR_MAX];
+	struct vsb proto;
 
 	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
 	AN(lsockname);
@@ -175,7 +178,17 @@ ses_vsl_socket(struct sess *sp, const char *lsockname)
 	    rport, sizeof rport);
 	VTCP_name(sess_local_addr(sp), laddr, sizeof laddr,
 	    lport, sizeof lport);
-	VSL(SLT_Begin, sp->vxid, "sess 0 HTTP/1");
+	AN(VSB_new(&proto, pbuf, sizeof pbuf, 0));
+#define PROTO(u, B, p, S)						\
+	if (sp->proto & B) {						\
+		if (VSB_len(&proto) != 0)				\
+			VSB_putc(&proto, '+');				\
+		VSB_cat(&proto, S);					\
+	}
+#include "tbl/listen_proto.h"
+#undef PROTO
+	(void)VSB_finish(&proto);
+	VSL(SLT_Begin, sp->vxid, "sess 0 %s", VSB_data(&proto));
 	VSL(SLT_SessOpen, sp->vxid, "%s %s %s %s %s %.6f %d",
 	    raddr, rport, lsockname, laddr, lport, sp->t_open, sp->fd);
 	memcpy(sess_client_addr0(sp), sess_remote_addr(sp),
diff --git a/bin/varnishd/common/common.h b/bin/varnishd/common/common.h
index 006df7a..2e8b607 100644
--- a/bin/varnishd/common/common.h
+++ b/bin/varnishd/common/common.h
@@ -88,6 +88,14 @@ struct cli;
 
 /**********************************************************************/
 
+enum listen_proto {
+#define PROTO(U, B, p, s) LPROTO_##U = B,
+#include "tbl/listen_proto.h"
+#undef PROTO
+};
+
+/**********************************************************************/
+
 /* Name of transient storage */
 #define TRANSIENT_STORAGE	"Transient"
 
diff --git a/bin/varnishd/common/heritage.h b/bin/varnishd/common/heritage.h
index b62a6ca..0ea6654 100644
--- a/bin/varnishd/common/heritage.h
+++ b/bin/varnishd/common/heritage.h
@@ -38,6 +38,7 @@ struct listen_sock {
 	int				sock;
 	char				*name;
 	struct vss_addr			*addr;
+	unsigned			proto;
 };
 
 VTAILQ_HEAD(listen_sock_head, listen_sock);
diff --git a/bin/varnishd/mgt/mgt_param_tweak.c b/bin/varnishd/mgt/mgt_param_tweak.c
index f989307..6cb338a 100644
--- a/bin/varnishd/mgt/mgt_param_tweak.c
+++ b/bin/varnishd/mgt/mgt_param_tweak.c
@@ -509,8 +509,37 @@ tweak_listen_address(struct vsb *vsb, const struct parspec *par,
 	for (i = 1; av[i] != NULL; i++) {
 		struct vss_addr **ta;
 		int j, n;
+		unsigned proto = 0;
+		char *p;
 
-		n = VSS_resolve(av[i], "http", &ta);
+		p = strchr(av[i], '@');
+		if (p != NULL) {
+#define PROTO(U, b, P, s)						\
+			if (strncmp(av[i], P, strlen(P)) == 0) {	\
+				proto = LPROTO_##U;			\
+			}
+#include "tbl/listen_proto.h"
+#undef PROTO
+			if (proto == LPROTO_PROXY)
+				proto |= LPROTO_HTTP1;
+		} else {
+			/* No protocol specified -> HTTP/1 */
+			proto = LPROTO_HTTP1;
+		}
+
+		if (p != NULL && proto == 0) {
+			VSB_printf(vsb, "Invalid protocol specifier ");
+			VSB_quote(vsb, av[i], -1, 0);
+			retval = -1;
+			break;
+		}
+
+		if (p != NULL)
+			p++;
+		else
+			p = av[i];
+
+		n = VSS_resolve(p, "http", &ta);
 		if (n == 0) {
 			VSB_printf(vsb, "Invalid listen address ");
 			VSB_quote(vsb, av[i], -1, 0);
@@ -524,6 +553,7 @@ tweak_listen_address(struct vsb *vsb, const struct parspec *par,
 			ls->addr = ta[j];
 			ls->name = strdup(av[i]);
 			AN(ls->name);
+			ls->proto = proto;
 			VTAILQ_INSERT_TAIL(&lsh, ls, list);
 		}
 		free(ta);
diff --git a/doc/sphinx/reference/varnishd.rst b/doc/sphinx/reference/varnishd.rst
index 8372086..4c4bab5 100644
--- a/doc/sphinx/reference/varnishd.rst
+++ b/doc/sphinx/reference/varnishd.rst
@@ -31,7 +31,7 @@ satisfy future requests for the same document.
 OPTIONS
 =======
 
--a address[:port][,address[:port][...]
+-a [protocol@]address[:port][,[protocol@]address[:port][...]
             Listen for client requests on the specified address and
             port.  The address can be a host name (“localhost”), an
             IPv4 dotted-quad (“127.0.0.1”), or an IPv6 address
@@ -40,7 +40,9 @@ OPTIONS
             IPv6 interfaces.  If port is not specified, the default
             HTTP port as listed in /etc/services is used.  Multiple
             listening addresses and ports can be specified as a
-            whitespace or comma -separated list.
+            whitespace or comma -separated list. An optional protocol
+            specifier can be provided by prepending either “proxy@” or
+            “http1@“ (default).
 
 -b host[:port]
             Use the specified host as backend server.  If port is not
diff --git a/include/tbl/listen_proto.h b/include/tbl/listen_proto.h
new file mode 100644
index 0000000..ec934ce
--- /dev/null
+++ b/include/tbl/listen_proto.h
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2015 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Dag Haavi Finstad <[email protected]>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Define the various protocols supported by the listen sockets.
+ */
+
+PROTO(HTTP1,	1<<0,	"http1",	"HTTP/1")
+PROTO(PROXY,	1<<1,	"proxy",	"PROXY")
-- 
2.1.4

From 77e8dcce5f0269d258c117b762422be066274c99 Mon Sep 17 00:00:00 2001
From: Dag Haavi Finstad <[email protected]>
Date: Fri, 9 Jan 2015 16:59:58 +0100
Subject: [PATCH 4/6] Set blocking mode prior to entering HTTP/1 FSM.

---
 bin/varnishd/cache/cache_session.c   | 17 +++++++++++++++++
 bin/varnishd/http1/cache_http1_fsm.c | 16 ----------------
 2 files changed, 17 insertions(+), 16 deletions(-)

diff --git a/bin/varnishd/cache/cache_session.c b/bin/varnishd/cache/cache_session.c
index f318f1e..e594d6a 100644
--- a/bin/varnishd/cache/cache_session.c
+++ b/bin/varnishd/cache/cache_session.c
@@ -107,9 +107,12 @@ static void
 ses_req_pool_task(struct worker *wrk, void *arg)
 {
 	struct req *req;
+	struct sess *sp;
 
 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
 	CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC);
+	sp = req->sp;
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
 
 	THR_SetRequest(req);
 	AZ(wrk->aws->r);
@@ -135,6 +138,20 @@ ses_sess_pool_task(struct worker *wrk, void *arg)
 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
 	CAST_OBJ_NOTNULL(sp, arg, SESS_MAGIC);
 
+	/*
+	 * Whenever we come in from the acceptor or waiter, we need to set
+	 * blocking mode.  It would be simpler to do this in the acceptor
+	 * or waiter, but we'd rather do the syscall in the worker thread.
+	 * On systems which return errors for ioctl, we close early
+	 */
+	if (VTCP_blocking(sp->fd)) {
+		wrk->stats->sess_closed++;
+		if (errno == ECONNRESET)
+			SES_Delete(sp, SC_REM_CLOSE, NAN);
+		else
+			SES_Delete(sp, SC_TX_ERROR, NAN);
+		return;
+	}
 	req = SES_GetReq(wrk, sp);
 	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
 
diff --git a/bin/varnishd/http1/cache_http1_fsm.c b/bin/varnishd/http1/cache_http1_fsm.c
index 62edd4b..8f6deb7 100644
--- a/bin/varnishd/http1/cache_http1_fsm.c
+++ b/bin/varnishd/http1/cache_http1_fsm.c
@@ -343,22 +343,6 @@ HTTP1_Session(struct worker *wrk, struct req *req)
 	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
 
 	/*
-	 * Whenever we come in from the acceptor or waiter, we need to set
-	 * blocking mode.  It would be simpler to do this in the acceptor
-	 * or waiter, but we'd rather do the syscall in the worker thread.
-	 * On systems which return errors for ioctl, we close early
-	 */
-	if (sp->sess_step == S_STP_NEWREQ && VTCP_blocking(sp->fd)) {
-		if (errno == ECONNRESET)
-			SES_Close(sp, SC_REM_CLOSE);
-		else
-			SES_Close(sp, SC_TX_ERROR);
-		sdr = http1_cleanup(sp, wrk, req);
-		assert(sdr == SESS_DONE_RET_GONE);
-		return;
-	}
-
-	/*
 	 * Return from waitinglist. Check to see if the remote has left.
 	 */
 	if (req->req_step == R_STP_LOOKUP && VTCP_check_hup(sp->fd)) {
-- 
2.1.4

From aeff4edafce80f80b4026d1b418197888200ce8a Mon Sep 17 00:00:00 2001
From: Dag Haavi Finstad <[email protected]>
Date: Fri, 30 Jan 2015 11:15:01 +0100
Subject: [PATCH 5/6] Preliminary PROXY v1/v2 support.

---
 bin/varnishd/Makefile.am               |   3 +
 bin/varnishd/cache/cache.h             |   3 +
 bin/varnishd/cache/cache_session.c     |  18 ++++
 bin/varnishd/proxy/cache_proxy.c       | 149 +++++++++++++++++++++++++++++++++
 bin/varnishd/proxy/cache_proxy1.c      | 138 ++++++++++++++++++++++++++++++
 bin/varnishd/proxy/cache_proxy2.c      | 109 ++++++++++++++++++++++++
 bin/varnishd/proxy/cache_proxy_proto.h |  76 +++++++++++++++++
 bin/varnishtest/tests/README           |   1 +
 bin/varnishtest/tests/o00000.vtc       |  53 ++++++++++++
 bin/varnishtest/tests/o00001.vtc       |  78 +++++++++++++++++
 include/tbl/steps.h                    |   1 +
 11 files changed, 629 insertions(+)
 create mode 100644 bin/varnishd/proxy/cache_proxy.c
 create mode 100644 bin/varnishd/proxy/cache_proxy1.c
 create mode 100644 bin/varnishd/proxy/cache_proxy2.c
 create mode 100644 bin/varnishd/proxy/cache_proxy_proto.h
 create mode 100644 bin/varnishtest/tests/o00000.vtc
 create mode 100644 bin/varnishtest/tests/o00001.vtc

diff --git a/bin/varnishd/Makefile.am b/bin/varnishd/Makefile.am
index b5aafb0..9d7aaf0 100644
--- a/bin/varnishd/Makefile.am
+++ b/bin/varnishd/Makefile.am
@@ -75,6 +75,9 @@ varnishd_SOURCES = \
 	mgt/mgt_sandbox_solaris.c \
 	mgt/mgt_shmem.c \
 	mgt/mgt_vcc.c \
+	proxy/cache_proxy.c \
+	proxy/cache_proxy1.c \
+	proxy/cache_proxy2.c \
 	storage/stevedore.c \
 	storage/stevedore_mgt.c \
 	storage/stevedore_utils.c \
diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h
index 9b8ddd3..d12d9a9 100644
--- a/bin/varnishd/cache/cache.h
+++ b/bin/varnishd/cache/cache.h
@@ -746,6 +746,9 @@ void HTTP1_Session(struct worker *, struct req *);
 extern const int HTTP1_Req[3];
 extern const int HTTP1_Resp[3];
 
+/* cache_proxy.c [PROXY] */
+int PROXY_Handle(struct worker *, struct sess *sp);
+
 /* cache_http1_deliver.c */
 void V1D_Deliver(struct req *, struct busyobj *);
 
diff --git a/bin/varnishd/cache/cache_session.c b/bin/varnishd/cache/cache_session.c
index e594d6a..0a09988 100644
--- a/bin/varnishd/cache/cache_session.c
+++ b/bin/varnishd/cache/cache_session.c
@@ -134,6 +134,7 @@ ses_sess_pool_task(struct worker *wrk, void *arg)
 {
 	struct req *req;
 	struct sess *sp;
+	int r;
 
 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
 	CAST_OBJ_NOTNULL(sp, arg, SESS_MAGIC);
@@ -152,6 +153,18 @@ ses_sess_pool_task(struct worker *wrk, void *arg)
 			SES_Delete(sp, SC_TX_ERROR, NAN);
 		return;
 	}
+
+	if (sp->sess_step == S_STP_PROXY) {
+		r = PROXY_Handle(wrk, sp);
+		if (r == -2)
+			return; /* Handed off to waiter. */
+		if (sp->fd == -1) {
+			wrk->stats->sess_closed++;
+			SES_Delete(sp, SC_NULL, NAN);
+			return;
+		}
+	}
+
 	req = SES_GetReq(wrk, sp);
 	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
 
@@ -246,6 +259,11 @@ SES_pool_accept_task(struct worker *wrk, void *arg)
 	lsockname = VCA_SetupSess(wrk, sp);
 	ses_vsl_socket(sp, lsockname);
 
+	if (sp->proto & LPROTO_PROXY)
+		sp->sess_step = S_STP_PROXY;
+	else
+		sp->sess_step = S_STP_NEWREQ;
+
 	ses_sess_pool_task(wrk, sp);
 }
 
diff --git a/bin/varnishd/proxy/cache_proxy.c b/bin/varnishd/proxy/cache_proxy.c
new file mode 100644
index 0000000..08a9d5d
--- /dev/null
+++ b/bin/varnishd/proxy/cache_proxy.c
@@ -0,0 +1,149 @@
+/*-
+ * Copyright (c) 2015 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Dag Haavi Finstad <[email protected]>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * PROXY protocol handling.
+ * http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt
+ *
+ */
+
+#include "config.h"
+
+#include <arpa/inet.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cache_proxy_proto.h"
+#include "cache/cache.h"
+
+#include "vsa.h"
+#include "vtim.h"
+
+void
+PROXY_write(struct sess *sp, int af, const struct sockaddr *c,
+    const struct sockaddr *s)
+{
+	unsigned sal;
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	assert(af == AF_INET || af == AF_INET6);
+	if (af == AF_INET) {
+		assert(c->sa_family == AF_INET && s->sa_family == AF_INET);
+		sal = sizeof (struct sockaddr_in);
+	} else {
+		assert(c->sa_family == AF_INET6 &&
+		    s->sa_family == AF_INET6);
+		sal = sizeof (struct sockaddr_in6);
+	}
+	AN(VSA_Build(sess_client_addr0(sp), c, sal));
+	AN(VSA_Build(sess_server_addr(sp), s, sal));
+	memcpy(sess_client_addr(sp), sess_client_addr0(sp),
+	    vsa_suckaddr_len);
+}
+
+static ssize_t
+proxy_parse(struct sess *sp, union proxy_hdr *hdr, ssize_t len)
+{
+	if (len >= PROXY1_MIN_LEN
+	    && memcmp(hdr->v1.line, PROXY1_SIG, PROXY1_SIG_LEN) == 0)
+		return (PROXY1_parse(sp, hdr, len));
+	else if (len >= PROXY2_MIN_LEN
+	    && memcmp(&hdr->v1.line, PROXY2_SIG, PROXY2_SIG_LEN) == 0)
+		return (PROXY2_parse(sp, hdr, len));
+	else
+		return (-1);
+}
+
+static ssize_t
+proxy_read(struct sess *sp, struct worker *wrk, union proxy_hdr *rxbuf)
+{
+	int tmo, i;
+	struct pollfd pfd[1];
+	double now, when;
+	ssize_t ret;
+
+	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	AZ(isnan(sp->t_idle));
+
+	tmo = (int)(1e3 * cache_param->timeout_linger);
+	while (1) {
+		pfd[0].fd = sp->fd;
+		pfd[0].events = POLLIN;
+		pfd[0].revents = 0;
+		i = poll(pfd, 1, tmo);
+		assert(i >= 0);
+		now = VTIM_real();
+		if (i != 0) {
+			ret = recv(sp->fd, rxbuf, sizeof *rxbuf,
+			    MSG_PEEK);
+			return (ret);
+		} else {
+			when = sp->t_idle + cache_param->timeout_linger;
+			tmo = (int)(1e3 * (when - now));
+			if (when < now || tmo == 0) {
+				wrk->stats->sess_herd++;
+				SES_Wait(sp);
+				return (-2);
+			}
+		}
+	}
+
+	/* Never reached. */
+	WRONG("Unreachable");
+}
+
+int
+PROXY_Handle(struct worker *wrk, struct sess *sp)
+{
+	union proxy_hdr hdr[1];
+	ssize_t sz, proxy_sz, r;
+
+	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+
+	sz = proxy_read(sp, wrk, hdr);
+	if (sz == -1)
+		SES_Close(sp, SC_REM_CLOSE);
+	else if (sz == -2)
+		return -2;
+
+	if (sp->fd != -1) {
+		proxy_sz = proxy_parse(sp, hdr, sz);
+		if (proxy_sz == -1)
+			SES_Close(sp, SC_RX_JUNK);
+	}
+
+	if (sp->fd != -1) {
+		/* Consume the size of the PROXY header. */
+		r = read(sp->fd, hdr, proxy_sz);
+		if (r <= 0)
+			SES_Close(sp, SC_REM_CLOSE);
+	}
+
+	/* Counters for bytes transmitted? req_hdrbytes? */
+	return (0);
+}
diff --git a/bin/varnishd/proxy/cache_proxy1.c b/bin/varnishd/proxy/cache_proxy1.c
new file mode 100644
index 0000000..1929570
--- /dev/null
+++ b/bin/varnishd/proxy/cache_proxy1.c
@@ -0,0 +1,138 @@
+/*-
+ * Copyright (c) 2015 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Dag Haavi Finstad <[email protected]>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Parsing routine for PROXY v1 header.
+ * http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt
+ *
+ */
+
+#include "config.h"
+
+#include <arpa/inet.h>
+#include <stdlib.h>
+
+#include "cache_proxy_proto.h"
+#include "cache/cache.h"
+
+#include "vsa.h"
+
+ssize_t
+PROXY1_parse(struct sess *sp, union proxy_hdr *hdr, ssize_t len)
+{
+	char *p, *q, *e;
+	ssize_t plen;
+	long int ptst;
+	int af;
+	struct sockaddr_storage client = {0}, server = {0};
+	struct sockaddr_in *sc = (struct sockaddr_in *) &client;
+	struct sockaddr_in *ss = (struct sockaddr_in *) &server;
+	struct sockaddr_in6 *sc6 = (struct sockaddr_in6 *) &client;
+	struct sockaddr_in6 *ss6 = (struct sockaddr_in6 *) &server;
+
+	p = hdr->v1.line;
+	e = memchr(p, '\r', len - 1);
+	if (e == NULL || e[1] != '\n')
+		return (-1);
+	*e = '\0';
+	plen = e + 2 - p;
+	p += PROXY1_SIG_LEN;
+	if (*p++ != ' ')
+		return (-1);
+	q = strchr(p, ' ');
+	if (q == NULL)
+		return (-1);
+
+	if (memcmp(p, "TCP4", q - p) == 0)
+		af = AF_INET;
+	else if (memcmp(p, "TCP6", q - p))
+		af = AF_INET6;
+	else if (memcmp(p, "UNKNOWN", q - p)) {
+		/* For UNKNOWN we must ignore the rest of the PROXY
+		 * header and use the address endpoints of the local
+		 * socket. */
+		return (plen);
+	} else {
+		return (-1);
+	}
+
+	assert (af == AF_INET || af == AF_INET6);
+	client.ss_family = server.ss_family = af;
+
+	/* Format: "TCPx ip_client ip_server port_client port_server" */
+	p += 4;
+	if (*p++ != ' ')
+		return (-1);
+	q = strchr(p, ' ');
+	if (q == NULL)
+		return (-1);
+	*q = '\0';
+	if (af == AF_INET) {
+		if (inet_pton(AF_INET, p, &sc->sin_addr) != 1)
+			return (-1);
+	} else {
+		if (inet_pton(AF_INET6, p, &sc6->sin6_addr) != 1)
+			return (-1);
+	}
+	p = q + 1;
+	q = strchr(p, ' ');
+	if (q == NULL)
+		return (-1);
+	*q = '\0';
+	if (af == AF_INET) {
+		if (inet_pton(AF_INET, p, &ss->sin_addr) != 1)
+			return (-1);
+	} else {
+		if (inet_pton(AF_INET6, p, &ss6->sin6_addr) != 1)
+			return (-1);
+	}
+	p = q + 1;
+	q = strchr(p, ' ');
+	if (q == NULL)
+		return (-1);
+	*q = '\0';
+	ptst = strtol(p, NULL, 10);
+	if (ptst < 0 || ptst > 65535)
+			return (-1);
+	if (af == AF_INET)
+		sc->sin_port = htons(ptst);
+	else
+		sc6->sin6_port = htons(ptst);
+	p = q + 1;
+	q = strchr(p, ' ');
+	ptst = strtol(p, NULL, 10);
+	if (ptst < 0 || ptst > 65535)
+		return (-1);
+	if (af == AF_INET)
+		ss->sin_port = htons(ptst);
+	else
+		ss6->sin6_port = htons(ptst);
+
+	PROXY_write(sp, af, (struct sockaddr *)&client,
+	    (struct sockaddr *)&server);
+
+	return (plen);
+}
diff --git a/bin/varnishd/proxy/cache_proxy2.c b/bin/varnishd/proxy/cache_proxy2.c
new file mode 100644
index 0000000..e3786b2
--- /dev/null
+++ b/bin/varnishd/proxy/cache_proxy2.c
@@ -0,0 +1,109 @@
+/*-
+ * Copyright (c) 2015 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Dag Haavi Finstad <[email protected]>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Parsing routine for PROXY v2 header.
+ * http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt
+ *
+ */
+
+#include "config.h"
+
+#include <arpa/inet.h>
+#include <stdlib.h>
+
+#include "cache_proxy_proto.h"
+#include "cache/cache.h"
+
+#include "vsa.h"
+
+ssize_t
+PROXY2_parse(struct sess *sp, union proxy_hdr *hdr, ssize_t len)
+{
+	ssize_t plen;
+	struct sockaddr_storage client = {0}, server = {0};
+	struct sockaddr_in *sc4 = (struct sockaddr_in *) &client;
+	struct sockaddr_in *ss4 = (struct sockaddr_in *) &server;
+	struct sockaddr_in6 *sc6 = (struct sockaddr_in6 *) &client;
+	struct sockaddr_in6 *ss6 = (struct sockaddr_in6 *) &server;
+
+	/* Version field: Highest four bits must be 0x2 */
+	if ((hdr->v2.ver_cmd & 0xf0) != 0x20)
+		return (-1);
+
+	hdr->v2.len = ntohs(hdr->v2.len);
+	plen = 16 + hdr->v2.len;
+	if (plen > len)
+		return (-1);
+
+	switch (hdr->v2.ver_cmd & 0xf) {
+	case 0x01: /* PROXY cmd */
+		switch(hdr->v2.fam) {
+		case 0x11: /* TCPv4  */
+			if (hdr->v2.len < sizeof(hdr->v2.addr.ip4))
+				return (-1);
+			sc4->sin_family = ss4->sin_family = AF_INET;
+			memcpy(&sc4->sin_addr, &hdr->v2.addr.ip4.src_addr,
+			    sizeof hdr->v2.addr.ip4.src_addr);
+			memcpy(&sc4->sin_port, &hdr->v2.addr.ip4.src_port,
+			    sizeof hdr->v2.addr.ip4.src_port);
+			memcpy(&ss4->sin_addr, &hdr->v2.addr.ip4.dst_addr,
+			    sizeof hdr->v2.addr.ip4.dst_addr);
+			memcpy(&ss4->sin_port, &hdr->v2.addr.ip4.dst_port,
+			    sizeof hdr->v2.addr.ip4.dst_port);
+			PROXY_write(sp, AF_INET, (struct sockaddr *)sc4,
+			    (struct sockaddr *)ss4);
+			return (plen);
+		case 0x21: /* TCPv6 */
+			if (hdr->v2.len < sizeof(hdr->v2.addr.ip6))
+				return (-1);
+			sc6->sin6_family = ss6->sin6_family = AF_INET6;
+			memcpy(&sc6->sin6_addr, &hdr->v2.addr.ip6.src_addr,
+			    sizeof hdr->v2.addr.ip6.src_addr);
+			memcpy(&sc6->sin6_port, &hdr->v2.addr.ip6.src_port,
+			    sizeof hdr->v2.addr.ip6.src_port);
+			memcpy(&ss6->sin6_addr, &hdr->v2.addr.ip6.dst_addr,
+			    sizeof hdr->v2.addr.ip6.dst_addr);
+			memcpy(&ss6->sin6_port, &hdr->v2.addr.ip6.dst_port,
+			    sizeof hdr->v2.addr.ip6.dst_port);
+			PROXY_write(sp, AF_INET6, (struct sockaddr *)sc6,
+			    (struct sockaddr *)ss6);
+			return (plen);
+		default:
+			/* Command not implemented. Must use local
+			 * addresses. Fall through to LOCAL case. */
+			;
+		}
+	case 0x00: /* LOCAL cmd */
+		/* must use the address endpoints of the local
+		 * socket. */
+		return (plen);
+	default:
+		return (-1);
+	}
+
+	WRONG("Unreachable");
+}
diff --git a/bin/varnishd/proxy/cache_proxy_proto.h b/bin/varnishd/proxy/cache_proxy_proto.h
new file mode 100644
index 0000000..a344b22
--- /dev/null
+++ b/bin/varnishd/proxy/cache_proxy_proto.h
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (c) 2015 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Dag Haavi Finstad <[email protected]>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <stdint.h>
+
+
+struct sess;
+
+#define PROXY1_SIG	"PROXY"
+#define PROXY1_SIG_LEN 	5
+#define PROXY1_MIN_LEN	8
+
+#define PROXY2_SIG	"\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
+#define PROXY2_SIG_LEN	12
+#define PROXY2_MIN_LEN	16
+
+union proxy_hdr {
+	struct {
+		char line[108];
+	} v1;
+	struct {
+		uint8_t		sig[12];
+		uint8_t		ver_cmd;
+		uint8_t		fam;
+		uint16_t	len;
+		union {
+			struct {
+				uint32_t src_addr;
+				uint32_t dst_addr;
+				uint16_t src_port;
+				uint16_t dst_port;
+			} ip4;
+			struct {
+				uint8_t  src_addr[16];
+				uint8_t  dst_addr[16];
+				uint16_t src_port;
+				uint16_t dst_port;
+			} ip6;
+			struct {
+				uint8_t src_addr[108];
+				uint8_t dst_addr[108];
+			} unx;
+		} addr;
+	} v2;
+};
+
+void PROXY_write(struct sess *, int af, const struct sockaddr *,
+    const struct sockaddr *);
+ssize_t PROXY1_parse(struct sess *, union proxy_hdr *, ssize_t);
+ssize_t PROXY2_parse(struct sess *, union proxy_hdr *, ssize_t);
diff --git a/bin/varnishtest/tests/README b/bin/varnishtest/tests/README
index a504ec5..e805a86 100644
--- a/bin/varnishtest/tests/README
+++ b/bin/varnishtest/tests/README
@@ -21,6 +21,7 @@ Naming scheme
 	id ~ [g] --> GZIP tests
 	id ~ [l] --> VSL tests
 	id ~ [m] --> VMOD tests excluding director
+	id ~ [o] --> PROXY tests
 	id ~ [p] --> Persistent tests
 	id ~ [r] --> Regression tests, same number as ticket
 	id ~ [s] --> Slow tests, expiry, grace etc.
diff --git a/bin/varnishtest/tests/o00000.vtc b/bin/varnishtest/tests/o00000.vtc
new file mode 100644
index 0000000..27973f8
--- /dev/null
+++ b/bin/varnishtest/tests/o00000.vtc
@@ -0,0 +1,53 @@
+varnishtest "PROXY v1 test"
+
+server s1 {
+	rxreq
+	txresp
+} -start
+
+varnish v1 -arg "-a [email protected]:0" -vcl+backend {
+	import ${vmod_std};
+
+	sub vcl_deliver {
+		set resp.http.clientip = client.ip;
+		set resp.http.clientport = std.port(client.ip);
+		set resp.http.serverip = server.ip;
+		set resp.http.serverport = std.port(server.ip);
+	}
+} -start
+
+client c1 {
+	send "PROXY TCP4 1.1.1.1 2.2.2.2 1 2\r\nGET / HTTP/1.1\nHost: example.com\n\n"
+	rxresp
+	expect resp.status == 200
+	expect resp.http.clientip == "1.1.1.1"
+	expect resp.http.clientport == "1"
+	expect resp.http.serverip == "2.2.2.2"
+	expect resp.http.serverport == "2"
+
+	txreq -hdr "Host: example.com"
+	rxresp
+	expect resp.status == 200
+	expect resp.http.clientip == "1.1.1.1"
+	expect resp.http.clientport == "1"
+	expect resp.http.serverip == "2.2.2.2"
+	expect resp.http.serverport == "2"
+} -run
+
+client c1 {
+	send "PROXY TCP4 3.3.3.3 4.4.4.4 1 2\r\nGET / HTTP/1.1\nHost: example.com\n\n"
+	rxresp
+	expect resp.status == 200
+	expect resp.http.clientip == "3.3.3.3"
+	expect resp.http.clientport == "1"
+	expect resp.http.serverip == "4.4.4.4"
+	expect resp.http.serverport == "2"
+
+	txreq -hdr "Host: example.com"
+	rxresp
+	expect resp.status == 200
+	expect resp.http.clientip == "3.3.3.3"
+	expect resp.http.clientport == "1"
+	expect resp.http.serverip == "4.4.4.4"
+	expect resp.http.serverport == "2"
+} -run
diff --git a/bin/varnishtest/tests/o00001.vtc b/bin/varnishtest/tests/o00001.vtc
new file mode 100644
index 0000000..eb6b726
--- /dev/null
+++ b/bin/varnishtest/tests/o00001.vtc
@@ -0,0 +1,78 @@
+varnishtest "PROXY v2 test"
+
+server s1 {
+	rxreq
+	txresp
+} -start
+
+varnish v1 -arg "-a [email protected]:0" -vcl+backend {
+	import ${vmod_std};
+
+	sub vcl_deliver {
+		set resp.http.clientip = client.ip;
+		set resp.http.clientport = std.port(client.ip);
+		set resp.http.serverip = server.ip;
+		set resp.http.serverport = std.port(server.ip);
+	}
+} -start
+
+client c1 {
+	sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a 21 11 00 0c 01 01 01 01 02 02 02 02 00 03 00 04"
+	txreq -hdr "Host: example.com"
+	rxresp
+	expect resp.status == 200
+	expect resp.http.clientip == "1.1.1.1"
+	expect resp.http.clientport == "3"
+	expect resp.http.serverip == "2.2.2.2"
+	expect resp.http.serverport == "4"
+
+	txreq -hdr "Host: example.com"
+	rxresp
+	expect resp.status == 200
+	expect resp.http.clientip == "1.1.1.1"
+	expect resp.http.clientport == "3"
+	expect resp.http.serverip == "2.2.2.2"
+	expect resp.http.serverport == "4"
+} -start
+
+client c2 {
+	sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a 21 21 00 24 00 01 00 01 00 01 00 00 00 00 00 00 00 00 00 01 00 02 00 02 00 02 00 00 00 00 00 00 00 00 00 02 00 05 00 06"
+	txreq -hdr "Host: example.com"
+	rxresp
+	expect resp.status == 200
+	expect resp.http.clientip == "1:1:1::1"
+	expect resp.http.clientport == "5"
+	expect resp.http.serverip == "2:2:2::2"
+	expect resp.http.serverport == "6"
+
+	txreq -hdr "Host: example.com"
+	rxresp
+	expect resp.status == 200
+	expect resp.http.clientip == "1:1:1::1"
+	expect resp.http.clientport == "5"
+	expect resp.http.serverip == "2:2:2::2"
+	expect resp.http.serverport == "6"
+} -start
+
+client c3 {
+	# Wrong protocol
+	txreq
+	expect_close
+} -start
+
+client c4 {
+	delay 0.5s
+	sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a 21 11 00 0c 01 01 01 01 02 02 02 02 00 03 00 04"
+	delay 0.5s
+	txreq -hdr "Host: example.com"
+	rxresp
+	expect resp.status == 200
+	expect resp.http.clientip == "1.1.1.1"
+	expect resp.http.clientport == "3"
+	expect resp.http.serverip == "2.2.2.2"
+	expect resp.http.serverport == "4"
+} -run
+
+client c1 -wait
+client c2 -wait
+client c3 -wait
diff --git a/include/tbl/steps.h b/include/tbl/steps.h
index bb02fb4..4769e8d 100644
--- a/include/tbl/steps.h
+++ b/include/tbl/steps.h
@@ -31,6 +31,7 @@
 /*lint -save -e525 -e539 */
 
 #ifdef SESS_STEP
+SESS_STEP(proxy,	PROXY)
 SESS_STEP(newreq,	NEWREQ)
 SESS_STEP(working,	WORKING)
 #endif
-- 
2.1.4

From 08a5221ed7b18060f17b05095c44d3e5ca81700c Mon Sep 17 00:00:00 2001
From: Dag Haavi Finstad <[email protected]>
Date: Fri, 30 Jan 2015 15:04:21 +0100
Subject: [PATCH 6/6] Update graphviz doc to reflect new S_STP_PROXY step.

---
 doc/graphviz/cache_http1_fsm.dot | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/doc/graphviz/cache_http1_fsm.dot b/doc/graphviz/cache_http1_fsm.dot
index 5021b91..8ebebe3 100644
--- a/doc/graphviz/cache_http1_fsm.dot
+++ b/doc/graphviz/cache_http1_fsm.dot
@@ -2,13 +2,18 @@
 		margin="0.5"
 		center="1"
 
+	acceptor -> PROXY_Handle [label=S_STP_PROXY]
 	acceptor -> http1_wait [label=S_STP_NEWREQ, align=center]
+	PROXY_Handle -> http1_wait [label=S_STP_NEWREQ]
 	hash -> CNT_Request [label="Busy object\nS_STP_WORKING\nR_STP_LOOKUP"
 		color=blue]
 	disembark -> hash [style=dotted, color=blue]
 	http1_wait -> CNT_Request [label="S_STP_WORKING\nR_STP_RECV"]
 	http1_wait -> disembark [label="Session close"]
 	http1_wait -> disembark [label="Timeout" color=green]
+	PROXY_Handle -> disembark [label="Timeout" color=purple]
+	PROXY_Handle -> disembark [label="Session close"]
+	waiter -> PROXY_Handle [color=purple]
 	disembark -> waiter [style=dotted, color=green]
 	waiter -> http1_wait [color=green]
 	CNT_Request -> disembark
-- 
2.1.4

_______________________________________________
varnish-dev mailing list
[email protected]
https://www.varnish-cache.org/lists/mailman/listinfo/varnish-dev

Reply via email to