]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: connection: Send out generic, user-defined server TLVs
authorAlexander Stephan <alexander.stephan@sap.com>
Sat, 28 Oct 2023 18:57:07 +0000 (20:57 +0200)
committerWilly Tarreau <w@1wt.eu>
Sat, 4 Nov 2023 03:56:59 +0000 (04:56 +0100)
To follow-up the implementation of the new set-proxy-v2-tlv-fmt
keyword in the server, the connection is updated to use the previously
allocated TLVs. If no value was specified, we send out an empty TLV.
As the feature is fully working with this commit, documentation and a
test for the server and default-server are added as well.

doc/configuration.txt
reg-tests/connection/proxy_protocol_send_generic.vtc [new file with mode: 0644]
src/connection.c

index 8d2fdde21ed24333d9a5a36248bb3c1585faa930..e7678a29a70fc4ede8c0813c616ed4ed32377d94 100644 (file)
@@ -16742,6 +16742,22 @@ send-proxy-v2
   of this version of the protocol. See also the "no-send-proxy-v2" option of
   this section and send-proxy" option of the "bind" keyword.
 
+set-proxy-v2-tlv-fmt(<id>) <fmt>
+  The "set-proxy-v2-tlv-fmt" parameter is used to send arbitrary PROXY protocol
+  version 2 TLVs. For the type (<id>) range of the defined TLV type please refer
+  to section 2.2.8. of the proxy protocol specification. However, the value can
+  be chosen freely as long as it does not exceed the maximum length of 65,535
+  bytes. It can also be used for forwarding TLVs by using the fetch "fc_pp_tlv"
+  to retrieve a received TLV from the frontend. It may be used as a server or
+  a default-server option. It must be used in combination with send-proxy-v2
+  such that PPv2 TLVs are actually sent out.
+
+  Example:
+  server srv1 192.168.1.1:80 send-proxy-v2 set-proxy-v2-tlv-fmt(0x20) %[fc_pp_tlv(0x20)]
+
+  In this case, we fetch the TLV with the type 0x20 as a string and set as the value
+  of a newly created TLV that also has the type 0x20.
+
 proxy-v2-options <option>[,<option>]*
   The "proxy-v2-options" parameter add options to send in PROXY protocol
   version 2 when "send-proxy-v2" is used. Options available are:
diff --git a/reg-tests/connection/proxy_protocol_send_generic.vtc b/reg-tests/connection/proxy_protocol_send_generic.vtc
new file mode 100644 (file)
index 0000000..f61bcae
--- /dev/null
@@ -0,0 +1,74 @@
+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
+
+        timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout client  "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout server  "${HAPROXY_TEST_TIMEOUT-5s}"
+
+    listen sender
+        bind "fd@${feS}"
+        server example ${h1_feR_addr}:${h1_feR_port} send-proxy-v2 set-proxy-v2-tlv-fmt(0xE1) %[str("foo")] set-proxy-v2-tlv-fmt(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)]
+
+        # Note that we do not check for an invalid TLV ID as that would result in an
+        # parser error anway.
+
+        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
+
+# Ensure that we achieve the same via a default-server.
+haproxy h2 -conf {
+    defaults
+        mode http
+        log global
+
+        timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout client  "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout server  "${HAPROXY_TEST_TIMEOUT-5s}"
+
+    listen sender
+        bind "fd@${feS}"
+        default-server send-proxy-v2 set-proxy-v2-tlv-fmt(0xE1) %[str("bar")]
+        server example ${h1_feR_addr}:${h1_feR_port}
+
+    listen receiver
+        bind "fd@${feR}" accept-proxy
+
+        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)]
+
+        http-request return status 200
+} -start
+
+
+client c2 -connect ${h2_feS_sock} {
+    txreq -url "/"
+    rxresp
+    expect resp.http.proxy_custom_tlv_a == "bar"
+} -run
index 59893fbb58ea8e57fb43a969f347a608daecc967..3a13c2542527f5c85833663bb70118f15ea61f7f 100644 (file)
@@ -1927,10 +1927,11 @@ 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;
 
        if (buf_len < PP2_HEADER_LEN)
                return 0;
@@ -2000,6 +2001,37 @@ 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;
+
+                       /* Users will always need to provide a value, in case of forwarding, they should use fc_pp_tlv.
+                        * for generic types. Otherwise, we will send an empty TLV.
+                        */
+                       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 {
+                               /* 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;