From a454d03d49c53b8dfb1c1dc020386b8ebdfcdc79 Mon Sep 17 00:00:00 2001
From: lijunlong <lijunlong@openresty.com>
Date: Mon, 10 Mar 2025 09:34:30 +0800
Subject: [PATCH] feature: add upstream proxy protocol v2 for stream module.

Co-author: chenri_shen@163.com
---
 ...ginx-1.27.1-stream_proxy_protocol_v2.patch | 630 ++++++++++++++++++
 util/mirror-tarballs                          |   6 +
 2 files changed, 636 insertions(+)
 create mode 100644 patches/nginx-1.27.1-stream_proxy_protocol_v2.patch

diff --git a/patches/nginx-1.27.1-stream_proxy_protocol_v2.patch b/patches/nginx-1.27.1-stream_proxy_protocol_v2.patch
new file mode 100644
index 0000000..88d5e10
--- /dev/null
+++ b/patches/nginx-1.27.1-stream_proxy_protocol_v2.patch
@@ -0,0 +1,630 @@
+diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c
+index 49888b9..27c927e 100644
+--- a/src/core/ngx_proxy_protocol.c
++++ b/src/core/ngx_proxy_protocol.c
+@@ -12,6 +12,39 @@
+ #define NGX_PROXY_PROTOCOL_AF_INET          1
+ #define NGX_PROXY_PROTOCOL_AF_INET6         2
+ 
++#define NGX_PROXY_PROTOCOL_V2_SIG              "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
++#define NGX_PROXY_PROTOCOL_V2_SIG_LEN          12
++#define NGX_PROXY_PROTOCOL_V2_HDR_LEN          16
++#define NGX_PROXY_PROTOCOL_V2_HDR_LEN_INET \
++                (NGX_PROXY_PROTOCOL_V2_HDR_LEN + (4 + 4 + 2 + 2))
++#define NGX_PROXY_PROTOCOL_V2_HDR_LEN_INET6 \
++                (NGX_PROXY_PROTOCOL_V2_HDR_LEN + (16 + 16 + 2 + 2))
++
++#define NGX_PROXY_PROTOCOL_V2_CMD_PROXY        (0x20 | 0x01)
++
++#define NGX_PROXY_PROTOCOL_V2_TRANS_STREAM     0x01
++
++#define NGX_PROXY_PROTOCOL_V2_FAM_UNSPEC       0x00
++#define NGX_PROXY_PROTOCOL_V2_FAM_INET         0x10
++#define NGX_PROXY_PROTOCOL_V2_FAM_INET6        0x20
++
++#define NGX_PROXY_PROTOCOL_V2_TYPE_ALPN             0x01
++#define NGX_PROXY_PROTOCOL_V2_TYPE_AUTHORITY        0x02 # Not implemented
++#define NGX_PROXY_PROTOCOL_V2_TYPE_CRC32C           0x03 # Not implemented
++#define NGX_PROXY_PROTOCOL_V2_TYPE_NOOP             0x04 # Not implemented
++#define NGX_PROXY_PROTOCOL_V2_TYPE_UNIQUE_ID        0x05 # Not implemented
++#define NGX_PROXY_PROTOCOL_V2_TYPE_SSL              0x20
++#define NGX_PROXY_PROTOCOL_V2_SUBTYPE_SSL_VERSION   0x21
++#define NGX_PROXY_PROTOCOL_V2_SUBTYPE_SSL_CN        0x22
++#define NGX_PROXY_PROTOCOL_V2_SUBTYPE_SSL_CIPHER    0x23
++#define NGX_PROXY_PROTOCOL_V2_SUBTYPE_SSL_SIG_ALG   0x24
++#define NGX_PROXY_PROTOCOL_V2_SUBTYPE_SSL_KEY_ALG   0x25
++#define NGX_PROXY_PROTOCOL_V2_TYPE_NETNS            0x30 # Not implemented
++
++#define NGX_PROXY_PROTOCOL_V2_CLIENT_SSL            0x01
++#define NGX_PROXY_PROTOCOL_V2_CLIENT_CERT_CONN      0x02
++#define NGX_PROXY_PROTOCOL_V2_CLIENT_CERT_SESS      0x04
++
+ 
+ #define ngx_proxy_protocol_parse_uint16(p)                                    \
+     ( ((uint16_t) (p)[0] << 8)                                                \
+@@ -66,6 +99,53 @@ typedef struct {
+ } ngx_proxy_protocol_tlv_entry_t;
+ 
+ 
++typedef union {
++    struct {
++        uint32_t          src_addr;
++        uint32_t          dst_addr;
++        uint16_t          src_port;
++        uint16_t          dst_port;
++    } ip4;
++    struct {
++        uint8_t           src_addr[16];
++        uint8_t           dst_addr[16];
++        uint16_t          src_port;
++        uint16_t          dst_port;
++    } ip6;
++} ngx_proxy_protocol_addrs_t;
++
++
++typedef struct {
++    u_char                        signature[12];
++    uint8_t                       version_command;
++    uint8_t                       family_transport;
++    uint16_t                      len;
++    ngx_proxy_protocol_addrs_t    addr;
++} ngx_proxy_protocol_v2_header_t;
++
++
++struct ngx_tlv_s {
++    uint8_t     type;
++    uint8_t     length_hi;
++    uint8_t     length_lo;
++    uint8_t     value[0];
++} __attribute__((packed));
++
++typedef struct ngx_tlv_s ngx_tlv_t;
++
++
++#if (NGX_STREAM_SSL)
++struct ngx_tlv_ssl_s {
++    ngx_tlv_t   tlv;
++    uint8_t     client;
++    uint32_t    verify;
++    uint8_t     sub_tlv[];
++} __attribute__((packed));
++
++typedef struct ngx_tlv_ssl_s ngx_tlv_ssl_t;
++#endif
++
++
+ 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,
+@@ -74,6 +154,15 @@ 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 u_char *ngx_proxy_protocol_v2_write(ngx_connection_t *c, u_char *buf,
++    u_char *last);
++#if (NGX_HAVE_INET6)
++static void ngx_v4tov6(struct in6_addr *sin6_addr, struct sockaddr *addr);
++#endif
++#if (NGX_STREAM_SSL)
++static u_char *ngx_copy_tlv(u_char *pos, u_char *last, u_char type,
++        u_char *value, uint16_t value_len);
++#endif
+ 
+ 
+ static ngx_proxy_protocol_tlv_entry_t  ngx_proxy_protocol_tlv_entries[] = {
+@@ -277,7 +366,8 @@ ngx_proxy_protocol_read_port(u_char *p, u_char *last, in_port_t *port,
+ 
+ 
+ u_char *
+-ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last)
++ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last,
++        ngx_uint_t pp_version)
+ {
+     ngx_uint_t  port, lport;
+ 
+@@ -291,6 +381,10 @@ ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last)
+         return NULL;
+     }
+ 
++    if (pp_version == 2) {
++        return ngx_proxy_protocol_v2_write(c, buf, last);
++    }
++
+     switch (c->sockaddr->sa_family) {
+ 
+     case AF_INET:
+@@ -612,3 +706,360 @@ ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs,
+ 
+     return NGX_DECLINED;
+ }
++
++
++static u_char *
++ngx_proxy_protocol_v2_write(ngx_connection_t *c, u_char *buf, u_char *last)
++{
++    struct sockaddr                 *src, *dst;
++    ngx_proxy_protocol_v2_header_t  *header;
++#if (NGX_HAVE_INET6)
++    struct in6_addr                  v6_tmp;
++    ngx_int_t                        v6_used;
++#endif
++#if (NGX_STREAM_SSL)
++    ngx_tlv_ssl_t                   *tlv;
++    u_char                          *value, *pos;
++    u_char                           kbuf[100];
++    const unsigned char             *data;
++    unsigned int                     data_len;
++
++    X509                            *crt;
++	EVP_PKEY                        *key;
++	const ASN1_OBJECT               *algorithm;
++    const char                      *s;
++
++    long                             rc;
++    size_t                           tlv_len;
++#endif
++    size_t                           len;
++
++    header = (ngx_proxy_protocol_v2_header_t *) buf;
++
++    header->len = 0;
++
++    src = c->sockaddr;
++    dst = c->local_sockaddr;
++
++    len = 0;
++
++#if (NGX_HAVE_INET6)
++    v6_used = 0;
++#endif
++
++    ngx_memcpy(header->signature, NGX_PROXY_PROTOCOL_V2_SIG,
++            NGX_PROXY_PROTOCOL_V2_SIG_LEN);
++
++    header->version_command = NGX_PROXY_PROTOCOL_V2_CMD_PROXY;
++    header->family_transport = NGX_PROXY_PROTOCOL_V2_TRANS_STREAM;
++
++    /** Addrs */
++
++    switch (src->sa_family) {
++
++    case AF_INET:
++
++        if (dst->sa_family == AF_INET) {
++
++            header->addr.ip4.src_addr =
++                    ((struct sockaddr_in *) src)->sin_addr.s_addr;
++            header->addr.ip4.src_port = ((struct sockaddr_in *) src)->sin_port;
++        }
++#if (NGX_HAVE_INET6)
++        else /** dst == AF_INET6 */{
++
++            ngx_v4tov6(&v6_tmp, src);
++            ngx_memcpy(header->addr.ip6.src_addr, &v6_tmp, 16);
++            header->addr.ip6.src_port = ((struct sockaddr_in *) src)->sin_port;
++        }
++#endif
++        break;
++
++#if (NGX_HAVE_INET6)
++    case AF_INET6:
++        v6_used = 1;
++
++        ngx_memcpy(header->addr.ip6.src_addr,
++                &((struct sockaddr_in6 *) src)->sin6_addr, 16);
++        header->addr.ip6.src_port = ((struct sockaddr_in6 *) src)->sin6_port;
++
++        break;
++#endif
++
++    default:
++        ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
++                    "PROXY protocol v2 unsupported src address family %ui",
++                    src->sa_family);
++        goto unspec;
++    };
++
++    switch (dst->sa_family) {
++    case AF_INET:
++
++        if (src->sa_family == AF_INET) {
++
++            header->addr.ip4.dst_addr =
++                ((struct sockaddr_in *) dst)->sin_addr.s_addr;
++            header->addr.ip4.dst_port = ((struct sockaddr_in *) dst)->sin_port;
++        }
++#if (NGX_HAVE_INET6)
++        else /** src == AF_INET6 */{
++
++            ngx_v4tov6(&v6_tmp, dst);
++            ngx_memcpy(header->addr.ip6.dst_addr, &v6_tmp, 16);
++            header->addr.ip6.dst_port = ((struct sockaddr_in *) dst)->sin_port;
++
++        }
++#endif
++        break;
++
++#if (NGX_HAVE_INET6)
++    case AF_INET6:
++        v6_used = 1;
++
++        ngx_memcpy(header->addr.ip6.dst_addr,
++                &((struct sockaddr_in6 *) dst)->sin6_addr, 16);
++        header->addr.ip6.dst_port = ((struct sockaddr_in6 *) dst)->sin6_port;
++
++        break;
++#endif
++
++    default:
++        ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
++                    "PROXY protocol v2 unsupported dest address family %ui",
++                    dst->sa_family);
++        goto unspec;
++    }
++
++#if (NGX_HAVE_INET6)
++    if (!v6_used) {
++        header->family_transport |= NGX_PROXY_PROTOCOL_V2_FAM_INET;
++        len = NGX_PROXY_PROTOCOL_V2_HDR_LEN_INET;
++
++    } else {
++        header->family_transport |= NGX_PROXY_PROTOCOL_V2_FAM_INET6;
++        len = NGX_PROXY_PROTOCOL_V2_HDR_LEN_INET6;
++
++    }
++#else
++    header->family_transport |= NGX_PROXY_PROTOCOL_V2_FAM_INET;
++    len = NGX_PROXY_PROTOCOL_V2_HDR_LEN_INET;
++#endif
++
++    /** SSL TLVs */
++#if (NGX_STREAM_SSL)
++
++    if (c->ssl != NULL) {
++
++        data = NULL;
++        data_len = 0;
++
++        tlv = (ngx_tlv_ssl_t *) (buf + len);
++        ngx_memzero(tlv, sizeof(ngx_tlv_ssl_t));
++
++        tlv->tlv.type = NGX_PROXY_PROTOCOL_V2_TYPE_SSL;
++        pos = buf + len + sizeof(ngx_tlv_ssl_t);
++
++        tlv->client |= NGX_PROXY_PROTOCOL_V2_CLIENT_SSL;
++
++#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
++        SSL_get0_alpn_selected(c->ssl->connection, &data, &data_len);
++
++#ifdef TLSEXT_TYPE_next_proto_neg
++        if (data_len == 0) {
++            SSL_get0_next_proto_negotiated(c->ssl->connection,
++                                           &data, &data_len);
++        }
++#endif
++
++#else /* TLSEXT_TYPE_next_proto_neg */
++        SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &data_len);
++#endif
++
++        if (data_len) {
++
++            pos = ngx_copy_tlv(pos, last,
++                               NGX_PROXY_PROTOCOL_V2_TYPE_ALPN,
++                               (u_char *) data, (uint16_t) data_len);
++            if (pos == NULL) {
++                return NULL;
++            }
++        }
++
++        value = (u_char *) SSL_get_version(c->ssl->connection);
++        if (value != NULL) {
++
++            pos = ngx_copy_tlv(pos, last,
++                               NGX_PROXY_PROTOCOL_V2_SUBTYPE_SSL_VERSION,
++                               value, ngx_strlen(value));
++            if (pos == NULL) {
++                return NULL;
++            }
++        }
++
++        crt = SSL_get0_peer_certificate(c->ssl->connection);
++        if (crt != NULL) {
++            tlv->client |= NGX_PROXY_PROTOCOL_V2_CLIENT_CERT_SESS;
++
++            rc = SSL_get_verify_result(c->ssl->connection);
++            tlv->verify = htonl(rc);
++
++            if (rc == X509_V_OK) {
++                if (ngx_ssl_ocsp_get_status(c, &s) == NGX_OK) {
++                    tlv->client |= NGX_PROXY_PROTOCOL_V2_CLIENT_CERT_CONN;
++                }
++            }
++
++            X509_NAME *subject_name_value = X509_get_subject_name(crt);
++            if(subject_name_value != NULL) {
++                int nid = OBJ_txt2nid("CN");
++                int index = X509_NAME_get_index_by_NID(subject_name_value, nid, -1);
++
++                X509_NAME_ENTRY *subject_name_cn_entry = X509_NAME_get_entry(subject_name_value, index);
++                if (subject_name_cn_entry) {
++                    ASN1_STRING *subject_name_cn_data_asn1 = X509_NAME_ENTRY_get_data(subject_name_cn_entry);
++
++                    if (subject_name_cn_data_asn1 != NULL) {
++                        value = (u_char *) ASN1_STRING_get0_data(subject_name_cn_data_asn1);
++                        if(value != NULL) {
++                            pos = ngx_copy_tlv(pos, last,
++                                        NGX_PROXY_PROTOCOL_V2_SUBTYPE_SSL_CN,
++                                        value, ngx_strlen(value));
++                            if (pos == NULL) {
++                                return NULL;
++                            }
++                        }
++                    }
++                }
++            }
++        }
++
++
++        crt = SSL_get_certificate(c->ssl->connection);
++        if (crt != NULL) {
++            key = X509_get_pubkey(crt);
++
++            /** Key */
++            if (key != NULL) {
++                switch (EVP_PKEY_base_id(key)) {
++                case EVP_PKEY_RSA:
++                    value = (u_char *) "RSA";
++                    break;
++                case EVP_PKEY_EC:
++                    value = (u_char *) "EC";
++                    break;
++                case EVP_PKEY_DSA:
++                    value = (u_char *) "DSA";
++                    break;
++                default:
++                    value = NULL;
++                    break;
++                }
++
++                if (value != NULL) {
++                    value = ngx_snprintf(kbuf, sizeof(kbuf) - 1, "%s%d%Z",
++                            value, EVP_PKEY_bits(key));
++
++                    pos = ngx_copy_tlv(pos, last,
++                                NGX_PROXY_PROTOCOL_V2_SUBTYPE_SSL_KEY_ALG,
++                                kbuf, ngx_strlen(kbuf));
++                }
++
++                EVP_PKEY_free(key);
++
++                if (pos == NULL) {
++                    return NULL;
++                }
++            }
++
++            /* ALG */
++            X509_ALGOR_get0(&algorithm, NULL, NULL, X509_get0_tbs_sigalg(crt));
++	        value = (u_char *) OBJ_nid2sn(OBJ_obj2nid(algorithm));
++
++            if (value != NULL) {
++
++                pos = ngx_copy_tlv(pos, last,
++                            NGX_PROXY_PROTOCOL_V2_SUBTYPE_SSL_SIG_ALG,
++                            value, ngx_strlen(value));
++                if (pos == NULL) {
++                    return NULL;
++                }
++            }
++        }
++
++        value = (u_char *) SSL_get_cipher_name(c->ssl->connection);
++        if (value != NULL) {
++
++            pos = ngx_copy_tlv(pos, last,
++                    NGX_PROXY_PROTOCOL_V2_SUBTYPE_SSL_CIPHER,
++                    value, ngx_strlen(value));
++            if (pos == NULL) {
++                return NULL;
++            }
++        }
++
++        tlv_len = pos - (buf + len);
++
++        tlv->tlv.length_hi = (uint16_t) (tlv_len - sizeof(ngx_tlv_t)) >> 8;
++        tlv->tlv.length_lo = (uint16_t) (tlv_len - sizeof(ngx_tlv_t)) & 0x00ff;
++
++        len = len + tlv_len;
++    }
++
++#endif
++
++    header->len = htons(len - NGX_PROXY_PROTOCOL_V2_HDR_LEN);
++    return buf + len;
++
++unspec:
++    header->family_transport |= NGX_PROXY_PROTOCOL_V2_FAM_UNSPEC;
++    header->len = 0;
++
++    return buf + NGX_PROXY_PROTOCOL_V2_HDR_LEN;
++}
++
++
++#if (NGX_HAVE_INET6)
++static void
++ngx_v4tov6(struct in6_addr *sin6_addr, struct sockaddr *addr)
++{
++    static const char rfc4291[] = { 0x00, 0x00, 0x00, 0x00,
++                                    0x00, 0x00, 0x00, 0x00,
++                                    0x00, 0x00, 0xFF, 0xFF };
++
++    struct in_addr tmp_addr, *sin_addr;
++
++    sin_addr = &((struct sockaddr_in *) addr)->sin_addr;
++
++    tmp_addr.s_addr = sin_addr->s_addr;
++    ngx_memcpy(sin6_addr->s6_addr, rfc4291, sizeof(rfc4291));
++    ngx_memcpy(sin6_addr->s6_addr + 12, &tmp_addr.s_addr, 4);
++}
++#endif
++
++
++#if (NGX_STREAM_SSL)
++
++static u_char *
++ngx_copy_tlv(u_char *pos, u_char *last, u_char type,
++        u_char *value, uint16_t value_len)
++{
++    ngx_tlv_t   *tlv;
++
++    if (last - pos < (long) sizeof(*tlv)) {
++        return NULL;
++    }
++
++    tlv = (ngx_tlv_t *) pos;
++
++    tlv->type = type;
++    tlv->length_hi = (uint16_t) value_len >> 8;
++    tlv->length_lo = (uint16_t) value_len & 0x00ff;
++    ngx_memcpy(tlv->value, value, value_len);
++
++    return pos + (value_len + sizeof(*tlv));
++}
++
++#endif
++
++
+diff --git a/src/core/ngx_proxy_protocol.h b/src/core/ngx_proxy_protocol.h
+index d1749f5..bc2e0a2 100644
+--- a/src/core/ngx_proxy_protocol.h
++++ b/src/core/ngx_proxy_protocol.h
+@@ -29,7 +29,7 @@ struct ngx_proxy_protocol_s {
+ 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,
+-    u_char *last);
++    u_char *last, ngx_uint_t pp_version);
+ ngx_int_t ngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name,
+     ngx_str_t *value);
+ 
+diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c
+index 82dca1e..0279866 100644
+--- a/src/stream/ngx_stream_proxy_module.c
++++ b/src/stream/ngx_stream_proxy_module.c
+@@ -30,7 +30,7 @@ typedef struct {
+     ngx_uint_t                       responses;
+     ngx_uint_t                       next_upstream_tries;
+     ngx_flag_t                       next_upstream;
+-    ngx_flag_t                       proxy_protocol;
++    ngx_uint_t                       proxy_protocol;
+     ngx_flag_t                       half_close;
+     ngx_stream_upstream_local_t     *local;
+     ngx_flag_t                       socket_keepalive;
+@@ -125,6 +125,14 @@ static ngx_conf_post_t  ngx_stream_proxy_ssl_conf_command_post =
+ #endif
+ 
+ 
++static ngx_conf_enum_t  ngx_stream_proxy_protocol[] = {
++    { ngx_string("off"), 0 },
++    { ngx_string("on"), 1 },
++    { ngx_string("v2"), 2 },
++    { ngx_null_string, 0 }
++};
++
++
+ static ngx_conf_deprecated_t  ngx_conf_deprecated_proxy_downstream_buffer = {
+     ngx_conf_deprecated, "proxy_downstream_buffer", "proxy_buffer_size"
+ };
+@@ -243,10 +251,10 @@ static ngx_command_t  ngx_stream_proxy_commands[] = {
+ 
+     { ngx_string("proxy_protocol"),
+       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
+-      ngx_conf_set_flag_slot,
++      ngx_conf_set_enum_slot,
+       NGX_STREAM_SRV_CONF_OFFSET,
+       offsetof(ngx_stream_proxy_srv_conf_t, proxy_protocol),
+-      NULL },
++      &ngx_stream_proxy_protocol },
+ 
+     { ngx_string("proxy_half_close"),
+       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
+@@ -914,7 +922,7 @@ ngx_stream_proxy_init_upstream(ngx_stream_session_t *s)
+             return;
+         }
+ 
+-        p = ngx_pnalloc(c->pool, NGX_PROXY_PROTOCOL_V1_MAX_HEADER);
++        p = ngx_pnalloc(c->pool, NGX_PROXY_PROTOCOL_MAX_HEADER);
+         if (p == NULL) {
+             ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
+             return;
+@@ -922,8 +930,8 @@ ngx_stream_proxy_init_upstream(ngx_stream_session_t *s)
+ 
+         cl->buf->pos = p;
+ 
+-        p = ngx_proxy_protocol_write(c, p,
+-                                     p + NGX_PROXY_PROTOCOL_V1_MAX_HEADER);
++        p = ngx_proxy_protocol_write(c, p, p + NGX_PROXY_PROTOCOL_MAX_HEADER,
++                                     u->proxy_protocol);
+         if (p == NULL) {
+             ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
+             return;
+@@ -963,7 +971,7 @@ static ngx_int_t
+ ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s)
+ {
+     u_char                  *p;
+-    u_char                   buf[NGX_PROXY_PROTOCOL_V1_MAX_HEADER];
++    u_char                   buf[NGX_PROXY_PROTOCOL_MAX_HEADER];
+     ssize_t                  n, size;
+     ngx_connection_t        *c, *pc;
+     ngx_stream_upstream_t   *u;
+@@ -976,15 +984,15 @@ ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s)
+     ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
+                    "stream proxy send PROXY protocol header");
+ 
+-    p = ngx_proxy_protocol_write(c, buf,
+-                                 buf + NGX_PROXY_PROTOCOL_V1_MAX_HEADER);
++    u = s->upstream;
++
++    p = ngx_proxy_protocol_write(c, buf, buf + NGX_PROXY_PROTOCOL_MAX_HEADER,
++                                 u->proxy_protocol);
+     if (p == NULL) {
+         ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
+         return NGX_ERROR;
+     }
+ 
+-    u = s->upstream;
+-
+     pc = u->peer.connection;
+ 
+     size = p - buf;
+@@ -2116,7 +2124,7 @@ ngx_stream_proxy_create_srv_conf(ngx_conf_t *cf)
+     conf->responses = NGX_CONF_UNSET_UINT;
+     conf->next_upstream_tries = NGX_CONF_UNSET_UINT;
+     conf->next_upstream = NGX_CONF_UNSET;
+-    conf->proxy_protocol = NGX_CONF_UNSET;
++    conf->proxy_protocol = NGX_CONF_UNSET_UINT;
+     conf->local = NGX_CONF_UNSET_PTR;
+     conf->socket_keepalive = NGX_CONF_UNSET;
+     conf->half_close = NGX_CONF_UNSET;
+@@ -2171,7 +2179,7 @@ ngx_stream_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+ 
+     ngx_conf_merge_value(conf->next_upstream, prev->next_upstream, 1);
+ 
+-    ngx_conf_merge_value(conf->proxy_protocol, prev->proxy_protocol, 0);
++    ngx_conf_merge_uint_value(conf->proxy_protocol, prev->proxy_protocol, 0);
+ 
+     ngx_conf_merge_ptr_value(conf->local, prev->local, NULL);
+ 
+diff --git a/src/stream/ngx_stream_upstream.h b/src/stream/ngx_stream_upstream.h
+index 25433d6..6df11df 100644
+--- a/src/stream/ngx_stream_upstream.h
++++ b/src/stream/ngx_stream_upstream.h
+@@ -141,7 +141,7 @@ typedef struct {
+     ngx_stream_upstream_resolved_t    *resolved;
+     ngx_stream_upstream_state_t       *state;
+     unsigned                           connected:1;
+-    unsigned                           proxy_protocol:1;
++    unsigned                           proxy_protocol:2;
+     unsigned                           half_closed:1;
+ } ngx_stream_upstream_t;
+ 
diff --git a/util/mirror-tarballs b/util/mirror-tarballs
index f6c271c..616b8bc 100755
--- a/util/mirror-tarballs
+++ b/util/mirror-tarballs
@@ -557,6 +557,12 @@ if [ "$answer" = "Y" ]; then
     patch -p1 < $root/patches/nginx-$main_ver-proc_exit_handler.patch || exit 1
 fi
 
+answer=`$root/util/ver-ge "$main_ver" 1.27.1`
+if [ "$answer" = "Y" ]; then
+    echo "$info_txt applying nginx-$main_ver-stream_proxy_protocol_v2 patch for nginx"
+    patch -p1 < $root/patches/nginx-$main_ver-stream_proxy_protocol_v2.patch || exit 1
+fi
+
 cp $root/html/index.html docs/html/ || exit 1
 cp $root/html/50x.html docs/html/ || exit 1