[master] a646c8f Add ProxyV2 support.

Poul-Henning Kamp phk at FreeBSD.org
Wed Apr 8 10:45:41 CEST 2015


commit a646c8f6cd66f638d5398da4f1a90c8e5b2f67c3
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date:   Wed Apr 8 08:45:18 2015 +0000

    Add ProxyV2 support.

diff --git a/bin/varnishd/proxy/cache_proxy_proto.c b/bin/varnishd/proxy/cache_proxy_proto.c
index 8c8eb07..c7ae516 100644
--- a/bin/varnishd/proxy/cache_proxy_proto.c
+++ b/bin/varnishd/proxy/cache_proxy_proto.c
@@ -32,6 +32,8 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 
+#include <netinet/in.h>
+
 #include <netdb.h>
 #include <stdlib.h>
 #include <string.h>
@@ -40,11 +42,7 @@
 
 #include "vend.h"
 #include "vsa.h"
-
-static const char vpx2_sig[] = {
-	'\r', '\n', '\r', '\n', '\0', '\r', '\n',
-	'Q', 'U', 'I', 'T', '\n',
-};
+#include "vtcp.h"
 
 /**********************************************************************
  * PROXY 1 protocol
@@ -60,7 +58,7 @@ vpx_proto1(const struct worker *wrk, struct req *req)
 	char *p, *q;
 	struct addrinfo hints, *res;
 	struct suckaddr *sa;
-	int pfam = 0;
+	int pfam = -1;
 
 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
 	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
@@ -156,21 +154,139 @@ vpx_proto1(const struct worker *wrk, struct req *req)
 	return (0);
 }
 
+/**********************************************************************
+ * PROXY 2 protocol
+ */
+
+static const char vpx2_sig[] = {
+	'\r', '\n', '\r', '\n', '\0', '\r', '\n',
+	'Q', 'U', 'I', 'T', '\n',
+};
+
 static int
 vpx_proto2(const struct worker *wrk, struct req *req)
 {
 	int l;
+	const uint8_t *p;
+	sa_family_t pfam = 0xff;
+	struct sockaddr_in sin4;
+	struct sockaddr_in6 sin6;
+	struct suckaddr *sa = NULL;
+	char hb[VTCP_ADDRBUFSIZE];
+	char pb[VTCP_PORTBUFSIZE];
 
 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
 	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
-	VSL(SLT_Debug, req->sp->fd, "PROXY2");
 
-	assert(req->htc->rxbuf_e - req->htc->rxbuf_b >= 16);
+	assert(req->htc->rxbuf_e - req->htc->rxbuf_b >= 16L);
 	l = vbe16dec(req->htc->rxbuf_b + 14);
-	req->htc->pipeline_b = req->htc->rxbuf_b + 16 + l;
+	assert(req->htc->rxbuf_e - req->htc->rxbuf_b >= 16L + l);
+	req->htc->pipeline_b = req->htc->rxbuf_b + 16L + l;
+	p = (const void *)req->htc->rxbuf_b;
+
+	/* Version @12 top half */
+	if ((p[12] >> 4) != 2) {
+		VSLb(req->vsl, SLT_ProxyGarbage,
+		    "PROXY2: bad version (%d)", p[12] >> 4);
+		return (-1);
+	}
+
+	/* Command @12 bottom half */
+	switch(p[12] & 0x0f) {
+	case 0x0:
+		/* Local connection from proxy, ignore addresses */
+		return (0);
+	case 0x1:
+		/* Proxied connection */
+		break;
+	default:
+		VSLb(req->vsl, SLT_ProxyGarbage,
+		    "PROXY2: bad command (%d)", p[12] & 0x0f);
+		return (-1);
+	}
+
+	/* Address family & protocol @13 */
+	switch(p[13]) {
+	case 0x00:
+		/* UNSPEC|UNSPEC, ignore proxy header */
+		VSLb(req->vsl, SLT_ProxyGarbage,
+		    "PROXY2: Ignoring UNSPEC|UNSPEC addresses");
+		return (0);
+	case 0x11:
+		/* IPv4|TCP */
+		pfam = AF_INET;
+		if (l < 12) {
+			VSLb(req->vsl, SLT_ProxyGarbage,
+			    "PROXY2: Ignoring short IPv4 addresses (%d)", l);
+			return (0);
+		}
+		break;
+	case 0x21:
+		/* IPv6|TCP */
+		pfam = AF_INET6;
+		if (l < 36) {
+			VSLb(req->vsl, SLT_ProxyGarbage,
+			    "PROXY2: Ignoring short IPv6 addresses (%d)", l);
+			return (0);
+		}
+		break;
+	default:
+		/* Ignore proxy header */
+		VSLb(req->vsl, SLT_ProxyGarbage,
+		    "PROXY2: Ignoring unsupported protocol (0x%02x)", p[13]);
+		return (0);
+	}
+
+	switch (pfam) {
+	case AF_INET:
+		memset(&sin4, 0, sizeof sin4);
+		sin4.sin_family = pfam;
+
+		/* dst/server */
+		memcpy(&sin4.sin_addr, p + 20, 4);
+		memcpy(&sin4.sin_port, p + 26, 2);
+		SES_Reserve_server_addr(req->sp, &sa);
+		AN(VSA_Build(sa, &sin4, sizeof sin4));
+
+		/* src/client */
+		memcpy(&sin4.sin_addr, p + 16, 4);
+		memcpy(&sin4.sin_port, p + 24, 2);
+		SES_Reserve_client_addr(req->sp, &sa);
+		AN(VSA_Build(sa, &sin4, sizeof sin4));
+		break;
+	case AF_INET6:
+		memset(&sin6, 0, sizeof sin6);
+		sin6.sin6_family = pfam;
+
+		/* dst/server */
+		memcpy(&sin6.sin6_addr, p + 32, 16);
+		memcpy(&sin6.sin6_port, p + 50, 2);
+		SES_Reserve_server_addr(req->sp, &sa);
+		AN(VSA_Build(sa, &sin6, sizeof sin6));
+
+		/* src/client */
+		memcpy(&sin6.sin6_addr, p + 16, 16);
+		memcpy(&sin6.sin6_port, p + 48, 2);
+		SES_Reserve_client_addr(req->sp, &sa);
+		AN(VSA_Build(sa, &sin6, sizeof sin6));
+		break;
+	default:
+		WRONG("Wrong pfam");
+	}
+
+	AN(sa);
+	VTCP_name(sa, hb, sizeof hb, pb, sizeof pb);
+	SES_Set_String_Attr(req->sp, SA_CLIENT_IP, hb);
+	SES_Set_String_Attr(req->sp, SA_CLIENT_PORT, pb);
+	VSLb(req->vsl, SLT_Debug, "PROXY2 %s %s", hb, pb);
+
 	return (0);
 }
 
+/**********************************************************************
+ * HTC_Rx completion detector
+ */
+
 static enum htc_status_e __match_proto__(htc_complete_f)
 vpx_complete(struct http_conn *htc)
 {
@@ -192,6 +308,8 @@ vpx_complete(struct http_conn *htc)
 		if (j == 0)
 			return (HTC_S_JUNK);
 		if (j == 1 && i == sizeof vpx1_sig) {
+			if (l > 107)
+				return (HTC_S_OVERFLOW);
 			if (strchr(p + i, '\n') == NULL)
 				return (HTC_S_MORE);
 			return (HTC_S_COMPLETE);
@@ -208,7 +326,6 @@ vpx_complete(struct http_conn *htc)
 	return (HTC_S_MORE);
 }
 
-
 void __match_proto__(task_func_t)
 VPX_Proto_Sess(struct worker *wrk, void *priv)
 {
diff --git a/bin/varnishtest/tests/o00001.vtc b/bin/varnishtest/tests/o00001.vtc
new file mode 100644
index 0000000..3d4e2f6
--- /dev/null
+++ b/bin/varnishtest/tests/o00001.vtc
@@ -0,0 +1,169 @@
+varnishtest "PROXY v2 test"
+
+server s1 {
+	# The server address is part of the hash-key
+	# so we need three responses
+	rxreq
+	expect req.http.x-forwarded-for == "127.0.0.1"
+	txresp -hdr "Obj: 1"
+	rxreq
+	expect req.http.x-forwarded-for == "1.2.3.4"
+	txresp -hdr "Obj: 2"
+	rxreq
+	expect req.http.x-forwarded-for == "102:304:506::d0e:f10"
+	txresp -hdr "Obj: 3"
+} -start
+
+varnish v1 -proto "PROXY" -vcl+backend {
+	import ${vmod_std};
+
+	acl fwd_client {
+		"1.2.3.4";
+		"102:304:506::d0e:f10";
+	}
+	acl fwd_server {
+		"5.6.7.8";
+		"8182:8384:8586::8d8e:8f80";
+	}
+
+	sub vcl_deliver {
+		set resp.http.li = local.ip;
+		set resp.http.lp = std.port(local.ip);
+		set resp.http.ri = remote.ip;
+		set resp.http.rp = std.port(remote.ip);
+		set resp.http.ci = client.ip;
+		set resp.http.cp = std.port(client.ip);
+		set resp.http.si = server.ip;
+		set resp.http.sp = std.port(server.ip);
+		set resp.http.fc = (client.ip ~ fwd_client);
+		set resp.http.fs = (server.ip ~ fwd_server);
+	}
+} -start
+
+client c1 {
+	# LOCAL command
+	sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+	sendhex "20 00 00 00"
+	txreq
+	rxresp
+	expect resp.status == 200
+	expect resp.http.si == "${v1_addr}"
+	expect resp.http.sp == "${v1_port}"
+	expect resp.http.ci == "127.0.0.1"
+} -run
+delay .1
+
+client c1 {
+	# unknown command
+	sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+	sendhex "22 00 00 00"
+	timeout 8
+	expect_close
+} -run
+delay .1
+
+client c1 {
+	# UNSPEC proto
+	sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+	sendhex "21 00 00 00"
+	txreq
+	rxresp
+	expect resp.status == 200
+	expect resp.http.si == "${v1_addr}"
+	expect resp.http.sp == "${v1_port}"
+	expect resp.http.ci == "127.0.0.1"
+} -run
+delay .1
+
+client c1 {
+	# unknown proto
+	sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+	sendhex "21 99 00 00"
+	txreq
+	rxresp
+	expect resp.status == 200
+	expect resp.http.si == "${v1_addr}"
+	expect resp.http.sp == "${v1_port}"
+	expect resp.http.ci == "127.0.0.1"
+} -run
+delay .1
+
+client c1 {
+	# short IPv4
+	sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+	sendhex "21 11 00 0b"
+	sendhex "01 02 03 04 05 06 07 08 09 0a 0b"
+	txreq
+	rxresp
+	expect resp.status == 200
+	expect resp.http.si == "${v1_addr}"
+	expect resp.http.sp == "${v1_port}"
+	expect resp.http.ci == "127.0.0.1"
+} -run
+delay .1
+
+client c1 {
+	# short IPv6
+	sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+	sendhex "21 21 00 23"
+	sendhex "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f"
+	sendhex "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f"
+	sendhex "01 02 03"
+	txreq
+	rxresp
+	expect resp.status == 200
+	expect resp.http.fs == false
+	expect resp.http.fc == false
+	expect resp.http.si == "${v1_addr}"
+	expect resp.http.sp == "${v1_port}"
+	expect resp.http.ci == "127.0.0.1"
+} -run
+delay .1
+
+client c1 {
+	# good IPv4
+	sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+	sendhex "21 11 00 0c"
+	sendhex "01 02 03 04"
+	sendhex "05 06 07 08"
+	sendhex "09 0a"
+	sendhex "0b 0c"
+	txreq
+	rxresp
+	expect resp.status == 200
+	expect resp.http.obj == 2
+	expect resp.http.fs == true
+	expect resp.http.fc == true
+	expect resp.http.ci == "1.2.3.4"
+	expect resp.http.cp == "2314"
+	expect resp.http.si == "5.6.7.8"
+	expect resp.http.sp == "2828"
+	expect resp.http.li == "${v1_addr}"
+	expect resp.http.lp == "${v1_port}"
+	expect resp.http.ri != "1.2.3.4"
+} -run
+delay .1
+
+client c1 {
+	# good IPv6
+	sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+	sendhex "21 21 00 24"
+	sendhex "01 02 03 04  05 06 00 00  00 00 00 00  0d 0e 0f 10"
+	sendhex "81 82 83 84  85 86 00 00  00 00 00 00  8d 8e 8f 80"
+	sendhex "09 0a"
+	sendhex "0b 0c"
+	txreq
+	rxresp
+	expect resp.status == 200
+	expect resp.http.obj == 3
+	expect resp.http.fs == true
+	expect resp.http.fc == true
+	expect resp.http.ci == "102:304:506::d0e:f10"
+	expect resp.http.cp == "2314"
+	expect resp.http.si == "8182:8384:8586::8d8e:8f80"
+	expect resp.http.sp == "2828"
+	expect resp.http.li == "${v1_addr}"
+	expect resp.http.lp == "${v1_port}"
+	expect resp.http.ri != "102:304:506::d0e:f10"
+} -run
+delay .1



More information about the varnish-commit mailing list