From 84608ed754c1a92e85e03036e8b0cd0949721ffb Mon Sep 17 00:00:00 2001
From: Alexander Stephan 
<alexander.step...@sap.com<mailto:alexander.step...@sap.com>>
Date: Fri, 15 Sep 2023 12:42:36 +0200
Subject: [PATCH 2/4] MEDIUM: connection: Send out generically allocated
 proxy-v2-options

This commit removes the previous limitations on the existing, fixed PPv2 TLV 
types
that can be sent out. This is done by leveraging the previously implemented
parsing. We iterate over the server TLV list and either forward or send a new
TLV, depending on whether a TLV exists in the remote connection.
---
 doc/configuration.txt                         | 20 ++++++++
 .../proxy_protocol_send_generic.vtc           | 35 +++++++++++++
 src/connection.c                              | 51 +++++++++++++++++--
 3 files changed, 103 insertions(+), 3 deletions(-)
 create mode 100644 reg-tests/connection/proxy_protocol_send_generic.vtc

diff --git a/doc/configuration.txt b/doc/configuration.txt
index 14f6b906d..aeff9e4db 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -16671,6 +16671,26 @@ proxy-v2-options <option>[,<option>]*
                 generated unique ID is also used for the first HTTP request
                 within a Keep-Alive connection.

+  Besides, you can specify the type of TLV numerically instead.
+
+  Example 1:
+  server example 127.0.0.1:2319 send-proxy-v2 proxy-v2-options crc32c,0xEE
+
+  The following logic applies: By default, the remote frontend connection is
+  searched for the value 0xEE. If found, it is simply forwarded. Otherwise,
+  a TLV with the ID "0xEE" and empty payload is sent out. In addition, crc32c
+  is also set here.
+
+  You can also specify a key-value pair <id>=<fmt> syntax. <id> represents the
+  8 bit TLV type field in the range of 0 to 255. It can be expressed in decimal
+  or hexadecimal format (prefixed by "0x").
+
+  Example 2:
+  server example_server 127.0.0.1:2319 send-proxy-v2 proxy-v2-options 
0xEE=%[str("foo")]
+
+  This will always send out the value "foo". Another common use case would be 
to
+  reference a variable.
+
 send-proxy-v2-ssl
   The "send-proxy-v2-ssl" parameter enforces use of the PROXY protocol version
   2 over any connection established to this server. The PROXY protocol informs
diff --git a/reg-tests/connection/proxy_protocol_send_generic.vtc 
b/reg-tests/connection/proxy_protocol_send_generic.vtc
new file mode 100644
index 000000000..e0bd15a1b
--- /dev/null
+++ b/reg-tests/connection/proxy_protocol_send_generic.vtc
@@ -0,0 +1,35 @@
+varnishtest "Check that generic TLV IDs are sent properly"
+
+#REQUIRE_VERSION=2.2
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+    defaults
+        mode http
+        log global
+
+    listen sender
+        bind "fd@${feS}"
+        server example ${h1_feR_addr}:${h1_feR_port} send-proxy-v2 
proxy-v2-options 0xE1=%[str("foo")],0xE2
+
+    listen receiver
+        bind "fd@${feR}" accept-proxy
+
+        # Check that the TLV value is set in the backend.
+        http-request set-var(txn.custom_tlv_a) fc_pp_tlv(0xE1)
+        http-after-response set-header proxy_custom_tlv_a 
%[var(txn.custom_tlv_a)]
+
+        # Check that TLVs without an value are sent out.
+        http-request set-var(txn.custom_tlv_b) fc_pp_tlv(0xE2)
+        http-after-response set-header proxy_custom_tlv_b 
%[var(txn.custom_tlv_b)]
+
+        http-request return status 200
+} -start
+
+client c1 -connect ${h1_feS_sock} {
+    txreq -url "/"
+    rxresp
+    expect resp.http.proxy_custom_tlv_a == "foo"
+    expect resp.http.proxy_custom_tlv_b == ""
+} -run
diff --git a/src/connection.c b/src/connection.c
index 5f7226aae..51584b1ed 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -22,6 +22,7 @@
 #include <haproxy/frontend.h>
 #include <haproxy/hash.h>
 #include <haproxy/list.h>
+#include <haproxy/log.h>
 #include <haproxy/log-t.h>
 #include <haproxy/namespace.h>
 #include <haproxy/net_helper.h>
@@ -548,7 +549,7 @@ struct connection *conn_new(void *target)
 /* Releases a connection previously allocated by conn_new() */
 void conn_free(struct connection *conn)
 {
-   struct conn_tlv_list *tlv, *tlv_back = NULL;
+   struct conn_tlv_list *tlv = NULL, *tlv_back = NULL;

    if (conn_is_back(conn))
        conn_backend_deinit(conn);
@@ -1920,10 +1921,12 @@ static int make_proxy_line_v2(char *buf, int buf_len, 
struct server *srv, struct
    int ret = 0;
    struct proxy_hdr_v2 *hdr = (struct proxy_hdr_v2 *)buf;
    struct sockaddr_storage null_addr = { .ss_family = 0 };
+   struct srv_pp_tlv_list *srv_tlv = NULL;
    const struct sockaddr_storage *src = &null_addr;
    const struct sockaddr_storage *dst = &null_addr;
-   const char *value;
-   int value_len;
+   const char *value = "";
+   int value_len = 0;
+   int found = 0;

    if (buf_len < PP2_HEADER_LEN)
        return 0;
@@ -1993,6 +1996,48 @@ static int make_proxy_line_v2(char *buf, int buf_len, 
struct server *srv, struct
        }
    }

+   if (strm) {
+       struct buffer *replace = NULL;
+
+       list_for_each_entry(srv_tlv, &srv->pp_tlvs, list) {
+           replace = NULL;
+
+           if (!LIST_ISEMPTY(&srv_tlv->fmt)) {
+               replace = alloc_trash_chunk();
+               if (unlikely(!replace))
+                   return 0;
+               replace->data = build_logline(strm, replace->area, 
replace->size, &srv_tlv->fmt);
+
+               if (unlikely((buf_len - ret) < sizeof(struct tlv))) {
+                   free_trash_chunk(replace);
+                   return 0;
+               }
+               ret += make_tlv(&buf[ret], (buf_len - ret), srv_tlv->type, 
replace->data, replace->area);
+               free_trash_chunk(replace);
+           }
+           else {
+               found = 0;
+               /* Fall back to use TLV value from remote connection, if 
available */
+               if (remote) {
+                   struct conn_tlv_list *remote_tlv = NULL;
+
+                   /* Search in remote connection TLV list */
+                   list_for_each_entry(remote_tlv, &remote->tlv_list, list) {
+                       if (remote_tlv->type != srv_tlv->type)
+                           continue;
+                       ret += make_tlv(&buf[ret], (buf_len - ret), 
srv_tlv->type, remote_tlv->len, remote_tlv->value);
+                       found = 1;
+                       break;
+                   }
+               }
+               if (!found)
+                   /* Create empty TLV as no value was specified */
+                   ret += make_tlv(&buf[ret], (buf_len - ret), srv_tlv->type, 
0, NULL);
+           }
+       }
+   }
+
+   /* Handle predefined TLVs as usual */
    if (srv->pp_opts & SRV_PP_V2_CRC32C) {
        uint32_t zero_crc32c = 0;

--
2.35.3

Reply via email to