changeset 7251:416953ef0428

Core: added processing of version 2 of the PROXY protocol. The protocol used on inbound connection is auto-detected and corresponding parser is used to extract passed addresses. TLV parameters are ignored. The maximum supported size of PROXY protocol header is 107 bytes (similar to version 1).
author Vladimir Homutov <vl@nginx.com>
date Thu, 22 Mar 2018 15:55:28 +0300
parents ec4d95eed062
children 7bdab16c55f1
files src/core/ngx_proxy_protocol.c
diffstat 1 files changed, 192 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/ngx_proxy_protocol.c
+++ b/src/core/ngx_proxy_protocol.c
@@ -9,6 +9,57 @@
 #include <ngx_core.h>
 
 
+#define NGX_PP_V2_SIGLEN         12
+#define NGX_PP_V2_CMD_PROXY      1
+#define NGX_PP_V2_STREAM         1
+
+#define NGX_PP_V2_AF_UNSPEC      0
+#define NGX_PP_V2_AF_INET        1
+#define NGX_PP_V2_AF_INET6       2
+
+
+#define ngx_pp_v2_get_u16(p)                                                  \
+    ( ((uint16_t) ((u_char *) (p))[0] << 8)                                   \
+    + (           ((u_char *) (p))[1]) )
+
+
+typedef struct {
+    u_char                       signature[NGX_PP_V2_SIGLEN];
+    u_char                       ver_cmd;
+    u_char                       fam_transp;
+    u_char                       len[2];
+} ngx_pp_v2_header_t;
+
+
+typedef struct {
+    u_char                       src[4];
+    u_char                       dst[4];
+    u_char                       sport[2];
+    u_char                       dport[2];
+} ngx_pp_v2_inet_addrs_t;
+
+
+typedef struct {
+    u_char                       src[16];
+    u_char                       dst[16];
+    u_char                       sport[2];
+    u_char                       dport[2];
+} ngx_pp_v2_inet6_addrs_t;
+
+
+typedef union {
+    ngx_pp_v2_inet_addrs_t       inet;
+    ngx_pp_v2_inet6_addrs_t      inet6;
+} ngx_pp_v2_addrs_t;
+
+
+static u_char *ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf,
+    u_char *last);
+
+static const u_char ngx_pp_v2_signature[NGX_PP_V2_SIGLEN] =
+    { 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A };
+
+
 u_char *
 ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last)
 {
@@ -19,6 +70,12 @@ ngx_proxy_protocol_read(ngx_connection_t
     p = buf;
     len = last - buf;
 
+    if (len >= sizeof(ngx_pp_v2_header_t)
+        && memcmp(p, ngx_pp_v2_signature, NGX_PP_V2_SIGLEN) == 0)
+    {
+        return ngx_proxy_protocol_v2_read(c, buf, last);
+    }
+
     if (len < 8 || ngx_strncmp(p, "PROXY ", 6) != 0) {
         goto invalid;
     }
@@ -166,3 +223,138 @@ ngx_proxy_protocol_write(ngx_connection_
 
     return ngx_slprintf(buf, last, " %ui %ui" CRLF, port, lport);
 }
+
+
+static u_char *
+ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf, u_char *last)
+{
+    u_char              *end;
+    size_t               len;
+    socklen_t            socklen;
+    ngx_str_t           *name;
+    ngx_uint_t           ver, cmd, family, transport;
+    ngx_sockaddr_t       sockaddr;
+    ngx_pp_v2_addrs_t   *addrs;
+    ngx_pp_v2_header_t  *hdr;
+
+    hdr = (ngx_pp_v2_header_t *) buf;
+
+    buf += sizeof(ngx_pp_v2_header_t);
+
+    ver = hdr->ver_cmd >> 4;
+
+    if (ver != 2) {
+        ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                      "unsupported PROXY protocol version: %ui", ver);
+        return NULL;
+    }
+
+    len = ngx_pp_v2_get_u16(hdr->len);
+
+    if ((size_t) (last - buf) < len) {
+        ngx_log_error(NGX_LOG_ERR, c->log, 0, "header is too large");
+        return NULL;
+    }
+
+    end = buf + len;
+
+    cmd = hdr->ver_cmd & 0x0F;
+
+    if (cmd != NGX_PP_V2_CMD_PROXY) {
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
+                       "PROXY protocol v2 unsupported cmd 0x%xi", cmd);
+        return end;
+    }
+
+    transport = hdr->fam_transp & 0x0F;
+
+    if (transport != NGX_PP_V2_STREAM) {
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
+                       "PROXY protocol v2 unsupported transport 0x%xi",
+                       transport);
+        return end;
+    }
+
+    family = hdr->fam_transp >> 4;
+
+    addrs = (ngx_pp_v2_addrs_t *) buf;
+
+    switch (family) {
+
+    case NGX_PP_V2_AF_UNSPEC:
+        ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0,
+                       "PROXY protocol v2 AF_UNSPEC ignored");
+        return end;
+
+    case NGX_PP_V2_AF_INET:
+
+        if ((size_t) (end - buf) < sizeof(ngx_pp_v2_inet_addrs_t)) {
+            return NULL;
+        }
+
+        sockaddr.sockaddr_in.sin_family = AF_INET;
+        sockaddr.sockaddr_in.sin_port = 0;
+        memcpy(&sockaddr.sockaddr_in.sin_addr, addrs->inet.src, 4);
+
+        c->proxy_protocol_port = ngx_pp_v2_get_u16(addrs->inet.sport);
+
+        socklen = sizeof(struct sockaddr_in);
+
+        buf += sizeof(ngx_pp_v2_inet_addrs_t);
+
+        break;
+
+#if (NGX_HAVE_INET6)
+
+    case NGX_PP_V2_AF_INET6:
+
+        if ((size_t) (end - buf) <  sizeof(ngx_pp_v2_inet6_addrs_t)) {
+            return NULL;
+        }
+
+        sockaddr.sockaddr_in6.sin6_family = AF_INET6;
+        sockaddr.sockaddr_in6.sin6_port = 0;
+        memcpy(&sockaddr.sockaddr_in6.sin6_addr, addrs->inet6.src, 16);
+
+        c->proxy_protocol_port = ngx_pp_v2_get_u16(addrs->inet6.sport);
+
+        socklen = sizeof(struct sockaddr_in6);
+
+        buf += sizeof(ngx_pp_v2_inet6_addrs_t);
+
+        break;
+
+#endif
+
+    default:
+
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
+                       "PROXY_protocol v2 unsupported address family "
+                       "0x%xi", family);
+        return end;
+    }
+
+    name = &c->proxy_protocol_addr;
+
+    name->data = ngx_pnalloc(c->pool, NGX_SOCKADDR_STRLEN);
+    if (name->data == NULL) {
+        return NULL;
+    }
+
+    name->len = ngx_sock_ntop(&sockaddr.sockaddr, socklen, name->data,
+                              NGX_SOCKADDR_STRLEN, 0);
+    if (name->len == 0) {
+        return NULL;
+    }
+
+    ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,
+                   "PROXY protocol v2 address: %V %i", name,
+                   (ngx_int_t) c->proxy_protocol_port);
+
+    if (buf < end) {
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
+                       "PROXY protocol v2 %z bytes tlv ignored", end - buf);
+    }
+
+    return end;
+}