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

Roman Arutyunyan arut at nginx.com
Thu Sep 1 09:47:07 UTC 2022


Hi,

> On 31 Aug 2022, at 19:52, Roman Arutyunyan <arut at nginx.com> 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.
> 
> 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.

A small update, thanks to Eran Kornblau.

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
@@ -446,7 +446,7 @@ ngx_proxy_protocol_v2_read(ngx_connectio
             }
 
             ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,
-                           "PROXY protocol v2 TLV type:0x%0xd len:%uz",
+                           "PROXY protocol v2 TLV type:0x%xd len:%uz",
                            tlv->type, len);
 
             t = ngx_list_push(pp_tlv);
@@ -462,7 +462,7 @@ ngx_proxy_protocol_v2_read(ngx_connectio
                 return NULL;
             }
 
-            t->key.len = ngx_sprintf(t->key.data, "%0xd", tlv->type)
+            t->key.len = ngx_sprintf(t->key.data, "%xd", tlv->type)
                          - t->key.data;
 
             t->value.data = ngx_pnalloc(c->pool, len);

Another possible update is support for arbitrary number of leading zeroes.
Not sure we need this right now though.

> 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,
> @@ -273,8 +279,11 @@ ngx_proxy_protocol_v2_read(ngx_connectio
>     size_t                              len;
>     socklen_t                           socklen;
>     ngx_uint_t                          version, command, family, transport;
> +    ngx_list_t                         *pp_tlv;
>     ngx_sockaddr_t                      src_sockaddr, dst_sockaddr;
> +    ngx_table_elt_t                    *t;
>     ngx_proxy_protocol_t               *pp;
> +    ngx_proxy_protocol_tlv_t           *tlv;
>     ngx_proxy_protocol_header_t        *header;
>     ngx_proxy_protocol_inet_addrs_t    *in;
> #if (NGX_HAVE_INET6)
> @@ -412,8 +421,63 @@ 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_tlv = ngx_list_create(c->pool, 1, sizeof(ngx_table_elt_t));
> +        if (pp_tlv == NULL) {
> +            return NULL;
> +        }
> +
> +        while (buf < end) {
> +            if ((size_t) (end - buf) < sizeof(ngx_proxy_protocol_tlv_t)) {
> +                ngx_log_error(NGX_LOG_ERR, c->log, 0,
> +                              "broken PROXY protocol TLV");
> +                return NULL;
> +            }
> +
> +            tlv = (ngx_proxy_protocol_tlv_t *) buf;
> +
> +            buf += sizeof(ngx_proxy_protocol_tlv_t);
> +
> +            len = ngx_proxy_protocol_parse_uint16(tlv->len);
> +
> +            if ((size_t) (end - buf) < len) {
> +                ngx_log_error(NGX_LOG_ERR, c->log, 0,
> +                              "broken PROXY protocol TLV");
> +                return NULL;
> +            }
> +
> +            ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,
> +                           "PROXY protocol v2 TLV type:0x%0xd len:%uz",
> +                           tlv->type, len);
> +
> +            t = ngx_list_push(pp_tlv);
> +            if (t == NULL) {
> +                return NULL;
> +            }
> +
> +            t->hash = 1;
> +            t->lowcase_key = NULL;
> +
> +            t->key.data = ngx_pnalloc(c->pool, 2);
> +            if (t->key.data == NULL) {
> +                return NULL;
> +            }
> +
> +            t->key.len = ngx_sprintf(t->key.data, "%0xd", tlv->type)
> +                         - t->key.data;
> +
> +            t->value.data = ngx_pnalloc(c->pool, len);
> +            if (t->value.data == NULL) {
> +                return NULL;
> +            }
> +
> +            ngx_memcpy(t->value.data, buf, len);
> +
> +            t->value.len = len;
> +
> +            buf += len;
> +        }
> +
> +        pp->tlv = pp_tlv;
>     }
> 
>     c->proxy_protocol = pp;
> 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_list_t         *tlv;
> };
> 
> 
> 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_0x"), 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,24 @@ 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_proxy_protocol_t  *pp;
> +
> +    pp = r->connection->proxy_protocol;
> +    if (pp == NULL || pp->tlv == NULL) {
> +        v->not_found = 1;
> +        return NGX_OK;
> +    }
> +
> +    return ngx_http_variable_unknown_header(r, v, (ngx_str_t *) data,
> +                                          &pp->tlv->part,
> +                                          sizeof("proxy_protocol_tlv_0x") - 1);
> +}
> +
> +
> +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,
> @@ -51,6 +53,10 @@ static ngx_int_t ngx_stream_variable_tim
> static ngx_int_t ngx_stream_variable_protocol(ngx_stream_session_t *s,
>     ngx_stream_variable_value_t *v, uintptr_t data);
> 
> +static ngx_int_t ngx_stream_variable_unknown_header(ngx_stream_session_t *s,
> +    ngx_stream_variable_value_t *v, ngx_str_t *var, ngx_list_part_t *part,
> +    size_t prefix);
> +
> 
> static ngx_stream_variable_t  ngx_stream_core_variables[] = {
> 
> @@ -79,6 +85,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,
> +      0, NGX_STREAM_VAR_PREFIX, 0 },
> +
>     { ngx_string("server_addr"), NULL,
>       ngx_stream_variable_server_addr, 0, 0, 0 },
> 
> @@ -622,6 +632,24 @@ 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_proxy_protocol_t  *pp;
> +
> +    pp = s->connection->proxy_protocol;
> +    if (pp == NULL || pp->tlv == NULL) {
> +        v->not_found = 1;
> +        return NGX_OK;
> +    }
> +
> +    return ngx_stream_variable_unknown_header(s, v, (ngx_str_t *) data,
> +                                          &pp->tlv->part,
> +                                          sizeof("proxy_protocol_tlv_0x") - 1);
> +}
> +
> +
> +static ngx_int_t
> ngx_stream_variable_server_addr(ngx_stream_session_t *s,
>     ngx_stream_variable_value_t *v, uintptr_t data)
> {
> @@ -911,6 +939,116 @@ ngx_stream_variable_protocol(ngx_stream_
> }
> 
> 
> +static ngx_int_t
> +ngx_stream_variable_unknown_header(ngx_stream_session_t *s,
> +    ngx_stream_variable_value_t *v, ngx_str_t *var,
> +    ngx_list_part_t *part, size_t prefix)
> +{
> +    u_char           *p, ch;
> +    size_t            len;
> +    ngx_uint_t        i, n;
> +    ngx_table_elt_t  *header, *h, **ph;
> +
> +    ph = &h;
> +#if (NGX_SUPPRESS_WARN)
> +    len = 0;
> +#endif
> +
> +    header = part->elts;
> +
> +    for (i = 0; /* void */ ; i++) {
> +
> +        if (i >= part->nelts) {
> +            if (part->next == NULL) {
> +                break;
> +            }
> +
> +            part = part->next;
> +            header = part->elts;
> +            i = 0;
> +        }
> +
> +        if (header[i].hash == 0) {
> +            continue;
> +        }
> +
> +        if (header[i].key.len != var->len - prefix) {
> +            continue;
> +        }
> +
> +        for (n = 0; n < var->len - prefix; n++) {
> +            ch = header[i].key.data[n];
> +
> +            if (ch >= 'A' && ch <= 'Z') {
> +                ch |= 0x20;
> +
> +            } else if (ch == '-') {
> +                ch = '_';
> +            }
> +
> +            if (var->data[n + prefix] != ch) {
> +                break;
> +            }
> +        }
> +
> +        if (n != var->len - prefix) {
> +            continue;
> +        }
> +
> +        len += header[i].value.len + 2;
> +
> +        *ph = &header[i];
> +        ph = &header[i].next;
> +    }
> +
> +    *ph = NULL;
> +
> +    if (h == NULL) {
> +        v->not_found = 1;
> +        return NGX_OK;
> +    }
> +
> +    len -= 2;
> +
> +    if (h->next == NULL) {
> +
> +        v->len = h->value.len;
> +        v->valid = 1;
> +        v->no_cacheable = 0;
> +        v->not_found = 0;
> +        v->data = h->value.data;
> +
> +        return NGX_OK;
> +    }
> +
> +    p = ngx_pnalloc(s->connection->pool, len);
> +    if (p == NULL) {
> +        return NGX_ERROR;
> +    }
> +
> +    v->len = len;
> +    v->valid = 1;
> +    v->no_cacheable = 0;
> +    v->not_found = 0;
> +    v->data = p;
> +
> +    for ( ;; ) {
> +
> +        p = ngx_copy(p, h->value.data, h->value.len);
> +
> +        if (h->next == NULL) {
> +            break;
> +        }
> +
> +        *p++ = ','; *p++ = ' ';
> +
> +        h = h->next;
> +    }
> +
> +    return NGX_OK;
> +}
> +
> +
> void *
> ngx_stream_map_find(ngx_stream_session_t *s, ngx_stream_map_t *map,
>     ngx_str_t *match)
> 
> _______________________________________________
> nginx-devel mailing list -- nginx-devel at nginx.org
> To unsubscribe send an email to nginx-devel-leave at nginx.org

----
Roman Arutyunyan
arut at nginx.com




-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.nginx.org/pipermail/nginx-devel/attachments/20220901/ad9f1257/attachment.htm>


More information about the nginx-devel mailing list