[PATCH] Core: support for reading PROXY protocol v2 TLVs
Roman Arutyunyan
arut at nginx.com
Tue Oct 11 13:01:11 UTC 2022
Hi,
On Tue, Oct 11, 2022 at 04:20:52AM +0300, Maxim Dounin wrote:
> Hello!
>
> On Tue, Sep 27, 2022 at 01:41:25PM +0400, Roman Arutyunyan wrote:
>
> [...]
>
> > # HG changeset patch
> > # User Roman Arutyunyan <arut at nginx.com>
> > # Date 1664263604 -14400
> > # Tue Sep 27 11:26:44 2022 +0400
> > # Node ID 38940ff7246574aa19a19c76b072073c34f191be
> > # Parent ba5cf8f73a2d0a3615565bf9545f3d65216a0530
> > PROXY protocol v2 TLV variables.
> >
> > The variables have prefix $proxy_protocol_tlv_ and are accessible by name
> > and by type. Examples are: $proxy_protocol_tlv_0x01, $proxy_protocol_tlv_alpn.
> >
> > 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
> > @@ -15,6 +15,12 @@
> >
> > #define ngx_proxy_protocol_parse_uint16(p) ((p)[0] << 8 | (p)[1])
> >
> > +#define ngx_proxy_protocol_parse_uint32(p) \
> > + ( ((uint32_t) (p)[0] << 24) \
> > + + ( (p)[1] << 16) \
> > + + ( (p)[2] << 8) \
> > + + ( (p)[3]) )
> > +
> >
> > typedef struct {
> > u_char signature[12];
> > @@ -40,6 +46,24 @@ typedef struct {
> > } ngx_proxy_protocol_inet6_addrs_t;
> >
> >
> > +typedef struct {
> > + u_char type;
> > + u_char len[2];
> > +} ngx_proxy_protocol_tlv_t;
> > +
> > +
> > +typedef struct {
> > + u_char client;
> > + u_char verify[4];
> > +} ngx_proxy_protocol_tlv_ssl_t;
> > +
> > +
> > +typedef struct {
> > + ngx_str_t name;
> > + ngx_uint_t type;
> > +} ngx_proxy_protocol_tlv_entry_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,
> > @@ -48,6 +72,26 @@ static u_char *ngx_proxy_protocol_v2_rea
> > u_char *last);
> >
> >
> > +static ngx_proxy_protocol_tlv_entry_t ngx_proxy_protocol_tlv_entries[] = {
> > + { ngx_string("alpn"), 0x01 },
> > + { ngx_string("authority"), 0x02 },
> > + { ngx_string("unique_id"), 0x05 },
> > + { ngx_string("ssl"), 0x20 },
> > + { ngx_string("netns"), 0x30 },
> > + { ngx_null_string, 0x00 }
> > +};
> > +
> > +
> > +static ngx_proxy_protocol_tlv_entry_t ngx_proxy_protocol_tlv_ssl_entries[] = {
> > + { ngx_string("version"), 0x21 },
> > + { ngx_string("cn"), 0x22 },
> > + { ngx_string("cipher"), 0x23 },
> > + { ngx_string("sig_alg"), 0x24 },
> > + { ngx_string("key_alg"), 0x25 },
> > + { ngx_null_string, 0x00 }
> > +};
> > +
> > +
> > u_char *
> > ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last)
> > {
> > @@ -412,11 +456,145 @@ 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)
>
> This probably can be made static and moved after
> ngx_proxy_protocol_get_tlv().
OK.
In fact, I kept this function public for vendor-specific TLV variables which
can be added by third-party modules. However, ngx_proxy_protocol_get_tlv()
seems to be enough despite extra tlv type parsing.
> > +{
> > + 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:%02xi", type);
> > +
> > + 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);
>
> I tend to think this is going to be too chatty on real load with
> multiple TLVs, and probably should be removed or #if 0'ed.
OK, removed.
> > +
> > + if (tlv->type == type) {
> > + value->data = p;
> > + value->len = len;
> > + return NGX_OK;
> > + }
> > +
> > + p += len;
> > + n -= len;
> > + }
> > +
> > + return NGX_DECLINED;
> > +}
> > +
> > +
> > +ngx_int_t
> > +ngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name,
> > + ngx_str_t *value)
> > +{
> > + u_char *p;
> > + size_t n;
> > + uint32_t verify;
> > + ngx_str_t ssl, *tlvs;
> > + ngx_int_t rc, type;
> > + ngx_proxy_protocol_tlv_ssl_t *tlv_ssl;
> > + ngx_proxy_protocol_tlv_entry_t *te;
> > +
> > + if (c->proxy_protocol == NULL) {
> > + return NGX_DECLINED;
> > + }
> > +
> > + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
> > + "PROXY protocol v2 get tlv \"%V\"", name);
> > +
> > + te = ngx_proxy_protocol_tlv_entries;
> > + tlvs = &c->proxy_protocol->tlvs;
> > +
> > + p = name->data;
> > + n = name->len;
> > +
> > + if (n >= 4 && p[0] == 's' && p[1] == 's' && p[2] == 'l' && p[3] == '_') {
> > +
> > + rc = ngx_proxy_protocol_lookup_tlv(c, tlvs, 0x20, &ssl);
> > + if (rc != NGX_OK) {
> > + return rc;
> > + }
> > +
> > + if (ssl.len < sizeof(ngx_proxy_protocol_tlv_ssl_t)) {
> > + return NGX_ERROR;
> > + }
> > +
> > + p += 4;
> > + n -= 4;
> > +
> > + if (n == 6 && ngx_strncmp(p, "verify", 6) == 0) {
> > +
> > + tlv_ssl = (ngx_proxy_protocol_tlv_ssl_t *) ssl.data;
> > + verify = ngx_proxy_protocol_parse_uint32(tlv_ssl->verify);
> > +
> > + value->data = ngx_pnalloc(c->pool, NGX_INT32_LEN);
> > + if (value->data == NULL) {
> > + return NGX_ERROR;
> > + }
> > +
> > + value->len = ngx_sprintf(value->data, "%uD", verify)
> > + - value->data;
> > + return NGX_OK;
> > + }
> > +
> > + ssl.data += sizeof(ngx_proxy_protocol_tlv_ssl_t);
> > + ssl.len -= sizeof(ngx_proxy_protocol_tlv_ssl_t);
> > +
> > + te = ngx_proxy_protocol_tlv_ssl_entries;
> > + tlvs = &ssl;
> > + }
> > +
> > + if (n >= 2 && p[0] == '0' && p[1] == 'x') {
> > +
> > + type = ngx_hextoi(p + 2, n - 2);
> > + if (type == NGX_ERROR) {
> > + return NGX_ERROR;
>
> This probably needs some error message.
OK, added.
> > + }
> > +
> > + return ngx_proxy_protocol_lookup_tlv(c, tlvs, type, value);
> > + }
> > +
> > + for ( /* void */ ; te->type; te++) {
> > + if (te->name.len == n && ngx_strncmp(te->name.data, p, n) == 0) {
> > + return ngx_proxy_protocol_lookup_tlv(c, tlvs, te->type, value);
> > + }
> > + }
> > +
> > + return NGX_DECLINED;
>
> Invalid/unknown names will silently result in empty variables. I
> tend to think this is going to be a problem, especially if we'll
> introduce additional names at some point. Some error instead
> might be a good idea.
And here.
> > +}
> > 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,10 @@ 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);
> > +ngx_int_t ngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name,
> > + 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(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_"), NULL,
> > + ngx_http_variable_proxy_protocol_tlv,
> > + 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,39 @@ ngx_http_variable_proxy_protocol_port(ng
> >
> >
> > 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 *name = (ngx_str_t *) data;
> > +
> > + ngx_int_t rc;
> > + ngx_str_t tlv, value;
> > +
> > + tlv.len = name->len - (sizeof("proxy_protocol_tlv_") - 1);
> > + tlv.data = name->data + sizeof("proxy_protocol_tlv_") - 1;
> > +
> > + rc = ngx_proxy_protocol_get_tlv(r->connection, &tlv, &value);
> > +
> > + if (rc == NGX_ERROR) {
> > + return NGX_ERROR;
> > + }
> > +
> > + if (rc == NGX_DECLINED) {
> > + v->not_found = 1;
> > + return NGX_OK;
> > + }
> > +
> > + v->len = value.len;
> > + v->valid = 1;
> > + v->no_cacheable = 0;
> > + v->not_found = 0;
> > + v->data = value.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(
> > + 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_"), NULL,
> > + ngx_stream_variable_proxy_protocol_tlv,
> > + 0, NGX_STREAM_VAR_PREFIX, 0 },
> > +
> > { ngx_string("server_addr"), NULL,
> > ngx_stream_variable_server_addr, 0, 0, 0 },
> >
> > @@ -622,6 +628,39 @@ ngx_stream_variable_proxy_protocol_port(
> >
> >
> > 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 *name = (ngx_str_t *) data;
> > +
> > + ngx_int_t rc;
> > + ngx_str_t tlv, value;
> > +
> > + tlv.len = name->len - (sizeof("proxy_protocol_tlv_") - 1);
> > + tlv.data = name->data + sizeof("proxy_protocol_tlv_") - 1;
> > +
> > + rc = ngx_proxy_protocol_get_tlv(s->connection, &tlv, &value);
> > +
> > + if (rc == NGX_ERROR) {
> > + return NGX_ERROR;
> > + }
> > +
> > + if (rc == NGX_DECLINED) {
> > + v->not_found = 1;
> > + return NGX_OK;
> > + }
> > +
> > + v->len = value.len;
> > + v->valid = 1;
> > + v->no_cacheable = 0;
> > + v->not_found = 0;
> > + v->data = value.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)
> > {
>
> Otherwise looks good.
[..]
--
Roman
-------------- next part --------------
# HG changeset patch
# User Roman Arutyunyan <arut at nginx.com>
# Date 1664263604 -14400
# Tue Sep 27 11:26:44 2022 +0400
# Node ID 2774f8d59b108635752f9f2dbe3a5394a3650b85
# Parent ba5cf8f73a2d0a3615565bf9545f3d65216a0530
PROXY protocol v2 TLV variables.
The variables have prefix $proxy_protocol_tlv_ and are accessible by name
and by type. Examples are: $proxy_protocol_tlv_0x01, $proxy_protocol_tlv_alpn.
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
@@ -15,6 +15,12 @@
#define ngx_proxy_protocol_parse_uint16(p) ((p)[0] << 8 | (p)[1])
+#define ngx_proxy_protocol_parse_uint32(p) \
+ ( ((uint32_t) (p)[0] << 24) \
+ + ( (p)[1] << 16) \
+ + ( (p)[2] << 8) \
+ + ( (p)[3]) )
+
typedef struct {
u_char signature[12];
@@ -40,12 +46,52 @@ typedef struct {
} ngx_proxy_protocol_inet6_addrs_t;
+typedef struct {
+ u_char type;
+ u_char len[2];
+} ngx_proxy_protocol_tlv_t;
+
+
+typedef struct {
+ u_char client;
+ u_char verify[4];
+} ngx_proxy_protocol_tlv_ssl_t;
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_uint_t type;
+} ngx_proxy_protocol_tlv_entry_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,
in_port_t *port, u_char sep);
static u_char *ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf,
u_char *last);
+static ngx_int_t ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c,
+ ngx_str_t *tlvs, ngx_uint_t type, ngx_str_t *value);
+
+
+static ngx_proxy_protocol_tlv_entry_t ngx_proxy_protocol_tlv_entries[] = {
+ { ngx_string("alpn"), 0x01 },
+ { ngx_string("authority"), 0x02 },
+ { ngx_string("unique_id"), 0x05 },
+ { ngx_string("ssl"), 0x20 },
+ { ngx_string("netns"), 0x30 },
+ { ngx_null_string, 0x00 }
+};
+
+
+static ngx_proxy_protocol_tlv_entry_t ngx_proxy_protocol_tlv_ssl_entries[] = {
+ { ngx_string("version"), 0x21 },
+ { ngx_string("cn"), 0x22 },
+ { ngx_string("cipher"), 0x23 },
+ { ngx_string("sig_alg"), 0x24 },
+ { ngx_string("key_alg"), 0x25 },
+ { ngx_null_string, 0x00 }
+};
u_char *
@@ -412,11 +458,147 @@ 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_get_tlv(ngx_connection_t *c, ngx_str_t *name,
+ ngx_str_t *value)
+{
+ u_char *p;
+ size_t n;
+ uint32_t verify;
+ ngx_str_t ssl, *tlvs;
+ ngx_int_t rc, type;
+ ngx_proxy_protocol_tlv_ssl_t *tlv_ssl;
+ ngx_proxy_protocol_tlv_entry_t *te;
+
+ if (c->proxy_protocol == NULL) {
+ return NGX_DECLINED;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
+ "PROXY protocol v2 get tlv \"%V\"", name);
+
+ te = ngx_proxy_protocol_tlv_entries;
+ tlvs = &c->proxy_protocol->tlvs;
+
+ p = name->data;
+ n = name->len;
+
+ if (n >= 4 && p[0] == 's' && p[1] == 's' && p[2] == 'l' && p[3] == '_') {
+
+ rc = ngx_proxy_protocol_lookup_tlv(c, tlvs, 0x20, &ssl);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ if (ssl.len < sizeof(ngx_proxy_protocol_tlv_ssl_t)) {
+ return NGX_ERROR;
+ }
+
+ p += 4;
+ n -= 4;
+
+ if (n == 6 && ngx_strncmp(p, "verify", 6) == 0) {
+
+ tlv_ssl = (ngx_proxy_protocol_tlv_ssl_t *) ssl.data;
+ verify = ngx_proxy_protocol_parse_uint32(tlv_ssl->verify);
+
+ value->data = ngx_pnalloc(c->pool, NGX_INT32_LEN);
+ if (value->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ value->len = ngx_sprintf(value->data, "%uD", verify)
+ - value->data;
+ return NGX_OK;
+ }
+
+ ssl.data += sizeof(ngx_proxy_protocol_tlv_ssl_t);
+ ssl.len -= sizeof(ngx_proxy_protocol_tlv_ssl_t);
+
+ te = ngx_proxy_protocol_tlv_ssl_entries;
+ tlvs = &ssl;
+ }
+
+ if (n >= 2 && p[0] == '0' && p[1] == 'x') {
+
+ type = ngx_hextoi(p + 2, n - 2);
+ if (type == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "invalid PROXY protocol TLV \"%V\"", name);
+ return NGX_ERROR;
+ }
+
+ return ngx_proxy_protocol_lookup_tlv(c, tlvs, type, value);
+ }
+
+ for ( /* void */ ; te->type; te++) {
+ if (te->name.len == n && ngx_strncmp(te->name.data, p, n) == 0) {
+ return ngx_proxy_protocol_lookup_tlv(c, tlvs, te->type, value);
+ }
+ }
+
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "PROXY protocol TLV \"%V\" not found", name);
+
+ return NGX_DECLINED;
+}
+
+
+static 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:%02xi", type);
+
+ 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;
+ }
+
+ 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_get_tlv(ngx_connection_t *c, ngx_str_t *name,
+ 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(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_"), NULL,
+ ngx_http_variable_proxy_protocol_tlv,
+ 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,39 @@ ngx_http_variable_proxy_protocol_port(ng
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 *name = (ngx_str_t *) data;
+
+ ngx_int_t rc;
+ ngx_str_t tlv, value;
+
+ tlv.len = name->len - (sizeof("proxy_protocol_tlv_") - 1);
+ tlv.data = name->data + sizeof("proxy_protocol_tlv_") - 1;
+
+ rc = ngx_proxy_protocol_get_tlv(r->connection, &tlv, &value);
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (rc == NGX_DECLINED) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->len = value.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = value.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(
+ 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_"), NULL,
+ ngx_stream_variable_proxy_protocol_tlv,
+ 0, NGX_STREAM_VAR_PREFIX, 0 },
+
{ ngx_string("server_addr"), NULL,
ngx_stream_variable_server_addr, 0, 0, 0 },
@@ -622,6 +628,39 @@ ngx_stream_variable_proxy_protocol_port(
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 *name = (ngx_str_t *) data;
+
+ ngx_int_t rc;
+ ngx_str_t tlv, value;
+
+ tlv.len = name->len - (sizeof("proxy_protocol_tlv_") - 1);
+ tlv.data = name->data + sizeof("proxy_protocol_tlv_") - 1;
+
+ rc = ngx_proxy_protocol_get_tlv(s->connection, &tlv, &value);
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (rc == NGX_DECLINED) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->len = value.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = value.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)
{
More information about the nginx-devel
mailing list