[PATCH] Core: support for reading PROXY protocol v2 TLVs

Roman Arutyunyan arut at nginx.com
Fri Sep 9 15:46:58 UTC 2022


Hi,

On Mon, Sep 05, 2022 at 06:58:38PM +0300, Maxim Dounin wrote:
> Hello!
> 
> On Mon, Sep 05, 2022 at 05:23:18PM +0400, Roman Arutyunyan wrote:
> 
> > Hi,
> > 
> > On Mon, Sep 05, 2022 at 03:52:49AM +0300, Maxim Dounin wrote:
> > > Hello!
> > > 
> > > On Wed, Aug 31, 2022 at 07:52:15PM +0400, Roman Arutyunyan wrote:
> > > 
> > > > # HG changeset patch
> > > > # User Roman Arutyunyan <arut at nginx.com>
> > > > # Date 1661436099 -14400
> > > > #      Thu Aug 25 18:01:39 2022 +0400
> > > > # Node ID 4b856f1dff939e4eb9c131e17af061cf2c38cfac
> > > > # Parent  069a4813e8d6d7ec662d282a10f5f7062ebd817f
> > > > Core: support for reading PROXY protocol v2 TLVs.
> > > 
> > > First of all, could you please provide details on the use case?  
> > > I've seen requests for writing proxy protocol TLVs to upstream 
> > > servers (see ticket #1639), but not yet seen any meaningful 
> > > reading requests.
> > 
> > The known cases are these:
> > 
> > - https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#proxy-protocol
> > - https://docs.microsoft.com/en-us/azure/private-link/private-link-service-overview#getting-connection-information-using-tcp-proxy-v2
> > - https://cloud.google.com/vpc/docs/configure-private-service-connect-producer#proxy-protocol
> > 
> > The data may need further parsing, but it can be done in njs or perl.
> 
> Thanks for the details.  So, basically, it's about vendor-specific 
> endpoint IDs.
> 
> > > > The TLV values are available in HTTP and Stream variables
> > > > $proxy_protocol_tlv_0xN, where N is a hexadecimal TLV type number with no
> > > > leading zeroes.
> > > 
> > > I can't say I like the "hexadecimal TLV type number with no 
> > > leading zeroes" approach, especially given that the specification 
> > > uses leading zeroes in TLV types.  With leading zeros might be 
> > > better, to match specification.

With on-demand approach this is no longer an issue.

> > > Also, it might worth the effort to actually add names for known 
> > > types instead or in addition to numbers.
> > 
> > This is indeed a good idea and we have such plans as a further extenion of this
> > work.  One of the problems is however that the abovementioned TLV variables
> > are specified in internal documents of AWS/Azure/GCP which are not standards.
> > They can be changed anytime, while we have to maintain those variables in
> > nginx.  Also, raw variables give more flexibility in supporting less known TLVs.
> 
> Of course I'm not suggesting to ditch raw variables, at least not 
> for unknown/non-standard values.  But for known/standard values it 
> should be easy enough to provide alternative names for easier use, 
> probably with type-specific parsing.
> 
> With on-demand parsing it would be trivial to support both 
> $proxy_protocol_tlv_alpn and $proxy_protocol_tlv_0x01.  Further, 
> it will be trivial to support $proxy_protocol_tlv_aws_vpc_id while 
> still providing $proxy_protocol_tlv_0xea for raw data.
> 
> > > Another question is PP2_TYPE_SSL, which is itself a complex 
> > > structure and a list of multiple subtypes.
> > 
> > This is an obvious one.  However we had exactly zero requests for this.
> 
> See the ticket mentioned above, it seems to be the main reason why 
> people want to see proxy protocol v2 to backends.
> 
> > > Provided 
> > > Given the above, not sure if the approach with early parsing and 
> > > header-like list as in the patch is the good idea.  Just 
> > > preserving TLVs as is and parsing them all during variable 
> > > evaluation might be easier and more efficient.
> > 
> > In this case, if we have two variables, say $proxy_protocol_tlv_ssl_{sni, alpn},
> > we'll parse the entire TLV block twice - once per variable evaluation.
> 
> Assuming you mean $proxy_protocol_{authority, alpn} (as these 
> aren't SSL subtypes), I actually see no difference in on-demand 
> parsing of the TLV block and looking for a header in the 
> pre-created list of headers.  Further, parsing the block for each 
> variable evaluation might be actually faster due to better 
> locality, and should simplify adding alternative names.
> 
> And a single TLV block certainly will be more optimal in terms of 
> memory usage due to no additional allocations.  Not to mention the 
> typical case when TLV variables aren't used at all.

Indeed on-demand parsing is quite easy and does not seems to be less efficient.

> > > Also, the idea of merging TLV values with identical types looks 
> > > wrong to me, especially given that many TLSs are binary.  
> > > Specification does not seem to define the behaviour here, 
> > > unfortunately.  As far as I understand, HAProxy itself still 
> > > doesn't implement PPv2 parsing, so there is not reference 
> > > implementation either.  On the other hand, it should be easy 
> > > enough to check all TLVs for duplicate by using a 256-bit bitmask 
> > > and reject connections if there are any duplicates.
> > 
> > This can be added, thanks.
> 
> Not sure it's actually needed though, especially given that proxy 
> protocol is only expected to be accepted from trusted sources 
> anyway.  It might be good enough to just assume there is only one 
> value with a given type.

Two patches attached:

- TLV on-demand parsing + raw variables
- standard named variables

--
Roman Arutyunyan
-------------- next part --------------
# HG changeset patch
# User Roman Arutyunyan <arut at nginx.com>
# Date 1662729947 -14400
#      Fri Sep 09 17:25:47 2022 +0400
# Node ID 80714f1b0f597ce5e530e7274457450db4587fc9
# Parent  069a4813e8d6d7ec662d282a10f5f7062ebd817f
Core: support for reading PROXY protocol v2 TLVs.

The TLV values are available in HTTP and Stream variables
$proxy_protocol_tlv_0xN, where N is a hexadecimal TLV type number.

diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c
--- a/src/core/ngx_proxy_protocol.c
+++ b/src/core/ngx_proxy_protocol.c
@@ -40,6 +40,12 @@ typedef struct {
 } ngx_proxy_protocol_inet6_addrs_t;
 
 
+typedef struct {
+    u_char                                  type;
+    u_char                                  len[2];
+} ngx_proxy_protocol_tlv_t;
+
+
 static u_char *ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p,
     u_char *last, ngx_str_t *addr);
 static u_char *ngx_proxy_protocol_read_port(u_char *p, u_char *last,
@@ -412,11 +418,73 @@ ngx_proxy_protocol_v2_read(ngx_connectio
                    &pp->src_addr, pp->src_port, &pp->dst_addr, pp->dst_port);
 
     if (buf < end) {
-        ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
-                       "PROXY protocol v2 %z bytes of tlv ignored", end - buf);
+        pp->tlvs.data = ngx_pnalloc(c->pool, end - buf);
+        if (pp->tlvs.data == NULL) {
+            return NULL;
+        }
+
+        ngx_memcpy(pp->tlvs.data, buf, end - buf);
+        pp->tlvs.len = end - buf;
     }
 
     c->proxy_protocol = pp;
 
     return end;
 }
+
+
+ngx_int_t
+ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs,
+    ngx_uint_t type, ngx_str_t *value)
+{
+    u_char                    *p;
+    size_t                     n, len;
+    ngx_proxy_protocol_tlv_t  *tlv;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
+                   "PROXY protocol v2 lookup tlv:%02xd", type);
+
+    if (tlvs == NULL) {
+        if (c->proxy_protocol == NULL) {
+            return NGX_DECLINED;
+        }
+
+        tlvs = &c->proxy_protocol->tlvs;
+    }
+
+    p = tlvs->data;
+    n = tlvs->len;
+
+    while (n) {
+        if (n < sizeof(ngx_proxy_protocol_tlv_t)) {
+            ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken PROXY protocol TLV");
+            return NGX_ERROR;
+        }
+
+        tlv = (ngx_proxy_protocol_tlv_t *) p;
+        len = ngx_proxy_protocol_parse_uint16(tlv->len);
+
+        p += sizeof(ngx_proxy_protocol_tlv_t);
+        n -= sizeof(ngx_proxy_protocol_tlv_t);
+
+        if (n < len) {
+            ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken PROXY protocol TLV");
+            return NGX_ERROR;
+        }
+
+        ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,
+                       "PROXY protocol v2 tlv:0x%02xd len:%uz",
+                       tlv->type, len);
+
+        if (tlv->type == type) {
+            value->data = p;
+            value->len = len;
+            return NGX_OK;
+        }
+
+        p += len;
+        n -= len;
+    }
+
+    return NGX_DECLINED;
+}
diff --git a/src/core/ngx_proxy_protocol.h b/src/core/ngx_proxy_protocol.h
--- a/src/core/ngx_proxy_protocol.h
+++ b/src/core/ngx_proxy_protocol.h
@@ -21,6 +21,7 @@ struct ngx_proxy_protocol_s {
     ngx_str_t           dst_addr;
     in_port_t           src_port;
     in_port_t           dst_port;
+    ngx_str_t           tlvs;
 };
 
 
@@ -28,6 +29,8 @@ u_char *ngx_proxy_protocol_read(ngx_conn
     u_char *last);
 u_char *ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf,
     u_char *last);
+ngx_int_t ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs,
+    ngx_uint_t type, ngx_str_t *value);
 
 
 #endif /* _NGX_PROXY_PROTOCOL_H_INCLUDED_ */
diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -61,6 +61,8 @@ static ngx_int_t ngx_http_variable_proxy
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_proxy_protocol_tlv_0x(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r,
@@ -214,6 +216,10 @@ static ngx_http_variable_t  ngx_http_cor
       ngx_http_variable_proxy_protocol_port,
       offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 },
 
+    { ngx_string("proxy_protocol_tlv_0x"), NULL,
+      ngx_http_variable_proxy_protocol_tlv_0x,
+      0, NGX_HTTP_VAR_PREFIX, 0 },
+
     { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 },
 
     { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 },
@@ -1387,6 +1393,46 @@ ngx_http_variable_proxy_protocol_port(ng
 
 
 static ngx_int_t
+ngx_http_variable_proxy_protocol_tlv_0x(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    ngx_str_t *name = (ngx_str_t *) data;
+
+    size_t      n;
+    u_char     *p;
+    ngx_str_t   s;
+    ngx_int_t   rc, type;
+
+    n = name->len - (sizeof("proxy_protocol_tlv_0x") - 1);
+    p = name->data + sizeof("proxy_protocol_tlv_0x") - 1;
+
+    type = ngx_hextoi(p, n);
+    if (type == NGX_ERROR) {
+        return NGX_ERROR;
+    }
+
+    rc = ngx_proxy_protocol_lookup_tlv(r->connection, NULL, type, &s);
+
+    if (rc == NGX_ERROR) {
+        return NGX_ERROR;
+    }
+
+    if (rc == NGX_DECLINED) {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    v->len = s.len;
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+    v->data = s.data;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_variable_server_addr(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
diff --git a/src/stream/ngx_stream_variables.c b/src/stream/ngx_stream_variables.c
--- a/src/stream/ngx_stream_variables.c
+++ b/src/stream/ngx_stream_variables.c
@@ -23,6 +23,8 @@ static ngx_int_t ngx_stream_variable_pro
     ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_stream_variable_proxy_protocol_port(
     ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_stream_variable_proxy_protocol_tlv_0x(
+    ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s,
     ngx_stream_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_stream_variable_server_port(ngx_stream_session_t *s,
@@ -79,6 +81,10 @@ static ngx_stream_variable_t  ngx_stream
       ngx_stream_variable_proxy_protocol_port,
       offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 },
 
+    { ngx_string("proxy_protocol_tlv_0x"), NULL,
+      ngx_stream_variable_proxy_protocol_tlv_0x,
+      0, NGX_STREAM_VAR_PREFIX, 0 },
+
     { ngx_string("server_addr"), NULL,
       ngx_stream_variable_server_addr, 0, 0, 0 },
 
@@ -622,6 +628,46 @@ ngx_stream_variable_proxy_protocol_port(
 
 
 static ngx_int_t
+ngx_stream_variable_proxy_protocol_tlv_0x(ngx_stream_session_t *s,
+    ngx_stream_variable_value_t *v, uintptr_t data)
+{
+    ngx_str_t *name = (ngx_str_t *) data;
+
+    size_t      n;
+    u_char     *p;
+    ngx_str_t   str;
+    ngx_int_t   rc, type;
+
+    n = name->len - (sizeof("proxy_protocol_tlv_0x") - 1);
+    p = name->data + sizeof("proxy_protocol_tlv_0x") - 1;
+
+    type = ngx_hextoi(p, n);
+    if (type == NGX_ERROR) {
+        return NGX_ERROR;
+    }
+
+    rc = ngx_proxy_protocol_lookup_tlv(s->connection, NULL, type, &str);
+
+    if (rc == NGX_ERROR) {
+        return NGX_ERROR;
+    }
+
+    if (rc == NGX_DECLINED) {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    v->len = str.len;
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+    v->data = str.data;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_stream_variable_server_addr(ngx_stream_session_t *s,
     ngx_stream_variable_value_t *v, uintptr_t data)
 {
-------------- next part --------------
# HG changeset patch
# User Roman Arutyunyan <arut at nginx.com>
# Date 1662718130 -14400
#      Fri Sep 09 14:08:50 2022 +0400
# Node ID 832f6c96b26c3009640072e3f4b1f0bf43e644d0
# Parent  80714f1b0f597ce5e530e7274457450db4587fc9
Standard PROXY protocol v2 TLV variables.

diff --git a/src/core/ngx_proxy_protocol.h b/src/core/ngx_proxy_protocol.h
--- a/src/core/ngx_proxy_protocol.h
+++ b/src/core/ngx_proxy_protocol.h
@@ -13,7 +13,18 @@
 #include <ngx_core.h>
 
 
-#define NGX_PROXY_PROTOCOL_MAX_HEADER  107
+#define NGX_PROXY_PROTOCOL_MAX_HEADER       107
+
+#define NGX_PROXY_PROTOCOL_TLV_ALPN         0x01
+#define NGX_PROXY_PROTOCOL_TLV_AUTHORITY    0x02
+#define NGX_PROXY_PROTOCOL_TLV_UNIQUE_ID    0x05
+#define NGX_PROXY_PROTOCOL_TLV_SSL          0x20
+#define NGX_PROXY_PROTOCOL_TLV_SSL_VERSION  0x21
+#define NGX_PROXY_PROTOCOL_TLV_SSL_CN       0x22
+#define NGX_PROXY_PROTOCOL_TLV_SSL_CIPHER   0x23
+#define NGX_PROXY_PROTOCOL_TLV_SSL_SIG_ALG  0x24
+#define NGX_PROXY_PROTOCOL_TLV_SSL_KEY_ALG  0x25
+#define NGX_PROXY_PROTOCOL_TLV_NETNS        0x30
 
 
 struct ngx_proxy_protocol_s {
@@ -25,6 +36,12 @@ struct ngx_proxy_protocol_s {
 };
 
 
+typedef struct {
+    u_char              client;
+    u_char              verify[4];
+} ngx_proxy_protocol_tlv_ssl_t;
+
+
 u_char *ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf,
     u_char *last);
 u_char *ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf,
diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -63,6 +63,10 @@ static ngx_int_t ngx_http_variable_proxy
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_proxy_protocol_tlv_0x(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_proxy_protocol_tlv_ssl(
+    ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r,
@@ -220,6 +224,42 @@ static ngx_http_variable_t  ngx_http_cor
       ngx_http_variable_proxy_protocol_tlv_0x,
       0, NGX_HTTP_VAR_PREFIX, 0 },
 
+    { ngx_string("proxy_protocol_tlv_alpn"), NULL,
+      ngx_http_variable_proxy_protocol_tlv,
+      NGX_PROXY_PROTOCOL_TLV_ALPN, 0, 0 },
+
+    { ngx_string("proxy_protocol_tlv_authority"), NULL,
+      ngx_http_variable_proxy_protocol_tlv,
+      NGX_PROXY_PROTOCOL_TLV_AUTHORITY, 0, 0 },
+
+    { ngx_string("proxy_protocol_tlv_unique_id"), NULL,
+      ngx_http_variable_proxy_protocol_tlv,
+      NGX_PROXY_PROTOCOL_TLV_UNIQUE_ID, 0, 0 },
+
+    { ngx_string("proxy_protocol_tlv_ssl_version"), NULL,
+      ngx_http_variable_proxy_protocol_tlv_ssl,
+      NGX_PROXY_PROTOCOL_TLV_SSL_VERSION, 0, 0 },
+
+    { ngx_string("proxy_protocol_tlv_ssl_cn"), NULL,
+      ngx_http_variable_proxy_protocol_tlv_ssl,
+      NGX_PROXY_PROTOCOL_TLV_SSL_CN, 0, 0 },
+
+    { ngx_string("proxy_protocol_tlv_ssl_cipher"), NULL,
+      ngx_http_variable_proxy_protocol_tlv_ssl,
+      NGX_PROXY_PROTOCOL_TLV_SSL_CIPHER, 0, 0 },
+
+    { ngx_string("proxy_protocol_tlv_ssl_sig_alg"), NULL,
+      ngx_http_variable_proxy_protocol_tlv_ssl,
+      NGX_PROXY_PROTOCOL_TLV_SSL_SIG_ALG, 0, 0 },
+
+    { ngx_string("proxy_protocol_tlv_ssl_key_alg"), NULL,
+      ngx_http_variable_proxy_protocol_tlv_ssl,
+      NGX_PROXY_PROTOCOL_TLV_SSL_KEY_ALG, 0, 0 },
+
+    { ngx_string("proxy_protocol_tlv_netns"), NULL,
+      ngx_http_variable_proxy_protocol_tlv,
+      NGX_PROXY_PROTOCOL_TLV_NETNS, 0, 0 },
+
     { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 },
 
     { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 },
@@ -1400,8 +1440,7 @@ ngx_http_variable_proxy_protocol_tlv_0x(
 
     size_t      n;
     u_char     *p;
-    ngx_str_t   s;
-    ngx_int_t   rc, type;
+    ngx_int_t   type;
 
     n = name->len - (sizeof("proxy_protocol_tlv_0x") - 1);
     p = name->data + sizeof("proxy_protocol_tlv_0x") - 1;
@@ -1411,7 +1450,58 @@ ngx_http_variable_proxy_protocol_tlv_0x(
         return NGX_ERROR;
     }
 
-    rc = ngx_proxy_protocol_lookup_tlv(r->connection, NULL, type, &s);
+    return ngx_http_variable_proxy_protocol_tlv(r, v, type);
+}
+
+
+static ngx_int_t
+ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    ngx_str_t  s;
+    ngx_int_t  rc;
+
+    rc = ngx_proxy_protocol_lookup_tlv(r->connection, NULL, data, &s);
+
+    if (rc == NGX_ERROR) {
+        return NGX_ERROR;
+    }
+
+    if (rc == NGX_DECLINED) {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    v->len = s.len;
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+    v->data = s.data;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_proxy_protocol_tlv_ssl(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    ngx_str_t  s, ssl;
+    ngx_int_t  rc;
+
+    rc = ngx_proxy_protocol_lookup_tlv(r->connection, NULL,
+                                       NGX_PROXY_PROTOCOL_TLV_SSL, &ssl);
+
+    if (rc == NGX_OK) {
+        if (ssl.len < sizeof(ngx_proxy_protocol_tlv_ssl_t)) {
+            return NGX_ERROR;
+        }
+
+        ssl.data += sizeof(ngx_proxy_protocol_tlv_ssl_t);
+        ssl.len -= sizeof(ngx_proxy_protocol_tlv_ssl_t);
+
+        rc = ngx_proxy_protocol_lookup_tlv(r->connection, &ssl, data, &s);
+    }
 
     if (rc == NGX_ERROR) {
         return NGX_ERROR;
diff --git a/src/stream/ngx_stream_variables.c b/src/stream/ngx_stream_variables.c
--- a/src/stream/ngx_stream_variables.c
+++ b/src/stream/ngx_stream_variables.c
@@ -25,6 +25,10 @@ static ngx_int_t ngx_stream_variable_pro
     ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_stream_variable_proxy_protocol_tlv_0x(
     ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_stream_variable_proxy_protocol_tlv(
+    ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_stream_variable_proxy_protocol_tlv_ssl(
+    ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s,
     ngx_stream_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_stream_variable_server_port(ngx_stream_session_t *s,
@@ -85,6 +89,42 @@ static ngx_stream_variable_t  ngx_stream
       ngx_stream_variable_proxy_protocol_tlv_0x,
       0, NGX_STREAM_VAR_PREFIX, 0 },
 
+    { ngx_string("proxy_protocol_tlv_alpn"), NULL,
+      ngx_stream_variable_proxy_protocol_tlv,
+      NGX_PROXY_PROTOCOL_TLV_ALPN, 0, 0 },
+
+    { ngx_string("proxy_protocol_tlv_authority"), NULL,
+      ngx_stream_variable_proxy_protocol_tlv,
+      NGX_PROXY_PROTOCOL_TLV_AUTHORITY, 0, 0 },
+
+    { ngx_string("proxy_protocol_tlv_unique_id"), NULL,
+      ngx_stream_variable_proxy_protocol_tlv,
+      NGX_PROXY_PROTOCOL_TLV_UNIQUE_ID, 0, 0 },
+
+    { ngx_string("proxy_protocol_tlv_ssl_version"), NULL,
+      ngx_stream_variable_proxy_protocol_tlv_ssl,
+      NGX_PROXY_PROTOCOL_TLV_SSL_VERSION, 0, 0 },
+
+    { ngx_string("proxy_protocol_tlv_ssl_cn"), NULL,
+      ngx_stream_variable_proxy_protocol_tlv_ssl,
+      NGX_PROXY_PROTOCOL_TLV_SSL_CN, 0, 0 },
+
+    { ngx_string("proxy_protocol_tlv_ssl_cipher"), NULL,
+      ngx_stream_variable_proxy_protocol_tlv_ssl,
+      NGX_PROXY_PROTOCOL_TLV_SSL_CIPHER, 0, 0 },
+
+    { ngx_string("proxy_protocol_tlv_ssl_sig_alg"), NULL,
+      ngx_stream_variable_proxy_protocol_tlv_ssl,
+      NGX_PROXY_PROTOCOL_TLV_SSL_SIG_ALG, 0, 0 },
+
+    { ngx_string("proxy_protocol_tlv_ssl_key_alg"), NULL,
+      ngx_stream_variable_proxy_protocol_tlv_ssl,
+      NGX_PROXY_PROTOCOL_TLV_SSL_KEY_ALG, 0, 0 },
+
+    { ngx_string("proxy_protocol_tlv_netns"), NULL,
+      ngx_stream_variable_proxy_protocol_tlv,
+      NGX_PROXY_PROTOCOL_TLV_NETNS, 0, 0 },
+
     { ngx_string("server_addr"), NULL,
       ngx_stream_variable_server_addr, 0, 0, 0 },
 
@@ -635,8 +675,7 @@ ngx_stream_variable_proxy_protocol_tlv_0
 
     size_t      n;
     u_char     *p;
-    ngx_str_t   str;
-    ngx_int_t   rc, type;
+    ngx_int_t   type;
 
     n = name->len - (sizeof("proxy_protocol_tlv_0x") - 1);
     p = name->data + sizeof("proxy_protocol_tlv_0x") - 1;
@@ -646,7 +685,58 @@ ngx_stream_variable_proxy_protocol_tlv_0
         return NGX_ERROR;
     }
 
-    rc = ngx_proxy_protocol_lookup_tlv(s->connection, NULL, type, &str);
+    return ngx_stream_variable_proxy_protocol_tlv(s, v, type);
+}
+
+
+static ngx_int_t
+ngx_stream_variable_proxy_protocol_tlv(ngx_stream_session_t *s,
+    ngx_stream_variable_value_t *v, uintptr_t data)
+{
+    ngx_str_t  str;
+    ngx_int_t  rc;
+
+    rc = ngx_proxy_protocol_lookup_tlv(s->connection, NULL, data, &str);
+
+    if (rc == NGX_ERROR) {
+        return NGX_ERROR;
+    }
+
+    if (rc == NGX_DECLINED) {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    v->len = str.len;
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+    v->data = str.data;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_variable_proxy_protocol_tlv_ssl(ngx_stream_session_t *s,
+    ngx_stream_variable_value_t *v, uintptr_t data)
+{
+    ngx_str_t  str, ssl;
+    ngx_int_t  rc;
+
+    rc = ngx_proxy_protocol_lookup_tlv(s->connection, NULL,
+                                       NGX_PROXY_PROTOCOL_TLV_SSL, &ssl);
+
+    if (rc == NGX_OK) {
+        if (ssl.len < sizeof(ngx_proxy_protocol_tlv_ssl_t)) {
+            return NGX_ERROR;
+        }
+
+        ssl.data += sizeof(ngx_proxy_protocol_tlv_ssl_t);
+        ssl.len -= sizeof(ngx_proxy_protocol_tlv_ssl_t);
+
+        rc = ngx_proxy_protocol_lookup_tlv(s->connection, &ssl, data, &str);
+    }
 
     if (rc == NGX_ERROR) {
         return NGX_ERROR;


More information about the nginx-devel mailing list