Willy,
while my primary motivation of this series is to *send* the unique ID,
instead of *receiving* it the patch to receive it comes first, because
it's super straight forward. I could basically copy the implementation
for the authority TLV.
For the whole unique ID processing I've used the new `ist` helpers, it
might make sense to update the authority processing to make use of ist
in a future CLEANUP patch.
I've added the new `struct connection` member at the end. Please check
whether you think that is the appropriate place for it or if it should
be moved somewhere else because of holes or caches.
I've also added a reg-test that verifies that pulling unique IDs works
properly. My first attempt was to use a LOCAL connection, because then
I would not need to send IP addresses in HEX encoding, however HAProxy
does not appear to read TLVs in LOCAL mode. I've attempted to find out
whether TLVs are supported for LOCAL mode or not in the proxy-protocol
specification, but it was not terribly clear. Supporting unique IDs in
LOCAL mode would definitely make sense to me. And supporting the CRC32
checksum would also make sense I guess.
So maybe the proxy protocol ingesting should be updated to process TLV
values for both PROXY and LOCAL mode? Do you have an opinion regarding
that?
Best regards
Tim Düsterhus
Apply with `git am --scissors` to automatically cut the commit message.
-- >8 --
This patch reads a proxy protocol v2 provided unique ID and makes it
available using the `fc_pp_unique_id` fetch.
---
doc/configuration.txt | 4 +++
include/proto/connection.h | 5 +++
include/types/connection.h | 1 +
reg-tests/stream/unique-id-from-proxy.vtc | 38 +++++++++++++++++++++
src/connection.c | 41 +++++++++++++++++++++++
5 files changed, 89 insertions(+)
create mode 100644 reg-tests/stream/unique-id-from-proxy.vtc
diff --git a/doc/configuration.txt b/doc/configuration.txt
index b508db217..a078942bb 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -15122,6 +15122,10 @@ fc_pp_authority : string
Returns the authority TLV sent by the client in the PROXY protocol header,
if any.
+fc_pp_unique_id : string
+ Returns the unique ID TLV sent by the client in the PROXY protocol header,
+ if any.
+
fc_rcvd_proxy : boolean
Returns true if the client initiated the connection with a PROXY protocol
header.
diff --git a/include/proto/connection.h b/include/proto/connection.h
index fb264d2b5..9b8eb8ad3 100644
--- a/include/proto/connection.h
+++ b/include/proto/connection.h
@@ -325,6 +325,7 @@ static inline void conn_init(struct connection *conn)
conn->src = NULL;
conn->dst = NULL;
conn->proxy_authority = NULL;
+ conn->proxy_unique_id = IST_NULL;
}
/* sets <owner> as the connection's owner */
@@ -458,6 +459,10 @@ static inline void conn_free(struct connection *conn)
pool_free(pool_head_authority, conn->proxy_authority);
conn->proxy_authority = NULL;
}
+ if (isttest(conn->proxy_unique_id)) {
+ pool_free(pool_head_uniqueid, conn->proxy_unique_id.ptr);
+ conn->proxy_unique_id = IST_NULL;
+ }
/* By convention we always place a NULL where the ctx points to if the
* mux is null. It may have been used to store the connection as a
diff --git a/include/types/connection.h b/include/types/connection.h
index 0c2d960b9..30cb895ff 100644
--- a/include/types/connection.h
+++ b/include/types/connection.h
@@ -469,6 +469,7 @@ struct connection {
char *proxy_authority; /* Value of authority TLV received via
PROXYv2 */
unsigned int idle_time; /* Time the connection was
added to the idle list, or 0 if not in the idle list */
uint8_t proxy_authority_len; /* Length of authority TLV received via
PROXYv2 */
+ struct ist proxy_unique_id; /* Value of the unique ID TLV received via
PROXYv2 */
};
/* PROTO token registration */
diff --git a/reg-tests/stream/unique-id-from-proxy.vtc
b/reg-tests/stream/unique-id-from-proxy.vtc
new file mode 100644
index 000000000..81ee3dea9
--- /dev/null
+++ b/reg-tests/stream/unique-id-from-proxy.vtc
@@ -0,0 +1,38 @@
+varnishtest "Check that we are able to read a unique-id from PROXYv2"
+
+#REQUIRE_VERSION=2.2
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ timeout connect 1s
+ timeout client 1s
+ timeout server 1s
+
+ frontend echo
+ bind "fd@${fe1}" accept-proxy
+ http-after-response set-header echo %[fc_pp_unique_id,hex]
+ http-request return status 200
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+ # PROXY v2 signature
+ sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+ # version + PROXY
+ sendhex "21"
+ # TCP4
+ sendhex "11"
+ # length of the address (12) + length of the TLV (8)
+ sendhex "00 14"
+ # 127.0.0.1 42 127.0.0.1 1337
+ sendhex "7F 00 00 01 7F 00 00 01 00 2A 05 39"
+ # PP2_TYPE_UNIQUE_ID + length of the value + "12345"
+ sendhex "05 00 05 31 32 33 34 35"
+
+ txreq -url "/"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.echo == "3132333435"
+} -run
diff --git a/src/connection.c b/src/connection.c
index 4e3a92f0c..4560110e3 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -755,6 +755,21 @@ int conn_recv_proxy(struct connection *conn, int flag)
conn->proxy_authority_len = tlv_len;
break;
}
+ case PP2_TYPE_UNIQUE_ID: {
+ const struct ist tlv = ist2((const char
*)tlv_packet->value, tlv_len);
+ if (tlv.len > UNIQUEID_LEN)
+ goto bad_header;
+ conn->proxy_unique_id.ptr =
pool_alloc(pool_head_uniqueid);
+ if (!isttest(conn->proxy_unique_id))
+ goto fail;
+ if (istcpy(&conn->proxy_unique_id, tlv,
UNIQUEID_LEN) < 0) {
+ /* This is technically unreachable,
because we verified above
+ * that the TLV value fits.
+ */
+ goto fail;
+ }
+ break;
+ }
default:
break;
}
@@ -1586,6 +1601,31 @@ int smp_fetch_fc_pp_authority(const struct arg *args,
struct sample *smp, const
return 1;
}
+/* fetch the unique ID TLV from a PROXY protocol header */
+int smp_fetch_fc_pp_unique_id(const struct arg *args, struct sample *smp,
const char *kw, void *private)
+{
+ struct connection *conn;
+
+ conn = objt_conn(smp->sess->origin);
+ if (!conn)
+ return 0;
+
+ if (conn->flags & CO_FL_WAIT_XPRT) {
+ smp->flags |= SMP_F_MAY_CHANGE;
+ return 0;
+ }
+
+ if (!isttest(conn->proxy_unique_id))
+ return 0;
+
+ smp->flags = 0;
+ smp->data.type = SMP_T_STR;
+ smp->data.u.str.area = conn->proxy_unique_id.ptr;
+ smp->data.u.str.data = conn->proxy_unique_id.len;
+
+ return 1;
+}
+
/* Note: must not be declared <const> as its list will be overwritten.
* Note: fetches that may return multiple types must be declared as the lowest
* common denominator, the type that can be casted into all other ones. For
@@ -1596,6 +1636,7 @@ static struct sample_fetch_kw_list sample_fetch_keywords
= {ILH, {
{ "bc_http_major", smp_fetch_fc_http_major, 0, NULL, SMP_T_SINT,
SMP_USE_L4SRV },
{ "fc_rcvd_proxy", smp_fetch_fc_rcvd_proxy, 0, NULL, SMP_T_BOOL,
SMP_USE_L4CLI },
{ "fc_pp_authority", smp_fetch_fc_pp_authority, 0, NULL, SMP_T_STR,
SMP_USE_L4CLI },
+ { "fc_pp_unique_id", smp_fetch_fc_pp_unique_id, 0, NULL, SMP_T_STR,
SMP_USE_L4CLI },
{ /* END */ },
}};
--
2.25.1