diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h index 84dd80442..5b0af63ff 100644 --- a/src/core/ngx_connection.h +++ b/src/core/ngx_connection.h @@ -9,6 +9,7 @@ #define _NGX_CONNECTION_H_INCLUDED_ +#include "ngx_proxy_protocol.h" #include #include @@ -148,6 +149,7 @@ struct ngx_connection_s { ngx_str_t addr_text; ngx_proxy_protocol_t *proxy_protocol; + ngx_proxy_protocol_tlv_buf_t ppv2_tlv; #if (NGX_QUIC || NGX_COMPAT) ngx_quic_stream_t *quic; diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c index 49888b986..739b59f47 100644 --- a/src/core/ngx_proxy_protocol.c +++ b/src/core/ngx_proxy_protocol.c @@ -3,15 +3,13 @@ * Copyright (C) Roman Arutyunyan * Copyright (C) Nginx, Inc. */ - - #include #include - #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_parse_uint16(p) \ ( ((uint16_t) (p)[0] << 8) \ @@ -321,6 +319,119 @@ ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last) return ngx_slprintf(buf, last, " %ui %ui" CRLF, port, lport); } +ngx_int_t +ngx_proxy_protocol_v2_build_tlv(ngx_connection_t *c, + ngx_proxy_v2_tlv *tlvs, + ngx_uint_t n) +{ + size_t size = 0; + u_char *p; + ngx_uint_t i; + + for (i = 0; i < n; i++) { + size += 3 + tlvs[i].length; /* type + length(2) + value */ + } + + p = ngx_palloc(c->pool, size); + if (p == NULL) { + return NGX_ERROR; + } + + c->ppv2_tlv.pos = p; + c->ppv2_tlv.len = size; + + for (i = 0; i < n; i++) { + *p++ = tlvs[i].type; + *p++ = (u_char) (tlvs[i].length >> 8); + *p++ = (u_char) (tlvs[i].length); + + p = ngx_cpymem(p, tlvs[i].value.data, tlvs[i].length); + } + + return NGX_OK; +} + + +u_char * +ngx_proxy_protocol_v2_write(ngx_connection_t *c, u_char *buf, u_char *last) { + u_char *buf_ptr = buf; + ngx_uint_t port, lport; + + if (last - buf < NGX_PROXY_PROTOCOL_V2_MAX_HEADER) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "the buffer is too small for proxy protocol v2"); + return NULL; + } + + buf_ptr = ngx_cpymem(buf_ptr, NGX_PROXY_PROTOCOL_V2_SIG, 12); + *buf_ptr++ = 0x21; + + port = ngx_inet_get_port(c->sockaddr); + lport = ngx_inet_get_port(c->local_sockaddr); + + switch (c->sockaddr->sa_family) { + case AF_INET: { + *buf_ptr++ = 0x11; + + *buf_ptr++ = 0x00; + *buf_ptr++ = 0x0C; + + struct sockaddr_in *sin; + sin = (struct sockaddr_in *) c->sockaddr; + + struct sockaddr_in *lsin; + lsin = (struct sockaddr_in *) c->local_sockaddr; + + buf_ptr = ngx_cpymem(buf_ptr, &sin->sin_addr.s_addr, 4); + buf_ptr = ngx_cpymem(buf_ptr, &lsin->sin_addr.s_addr, 4); + + *buf_ptr++ = (u_char) (port >> 8); + *buf_ptr++ = (u_char) port; + + *buf_ptr++ = (u_char) (lport >> 8); + *buf_ptr++ = (u_char) lport; + + break; + } +#if (NGX_HAVE_INET6) + case AF_INET6: { + *buf_ptr++ = 0x00; + *buf_ptr++ = 0x24; + + struct sockaddr_in6 *sin6; + sin6 = (struct sockaddr_in6 *) c->sockaddr; + + struct sockaddr_in6 *lsin6; + lsin6 = (struct sockaddr_in6 *) c->local_sockaddr; + + buf_ptr = ngx_cpymem(buf_ptr, &sin6->sin6_addr, 16); + buf_ptr = ngx_cpymem(buf_ptr, &lsin6->sin6_addr, 16); + + *buf_ptr++ = (u_char)(port >> 8); + *buf_ptr++ = (u_char) port; + + *buf_ptr++ = (u_char)(lport >> 8); + *buf_ptr++ = (u_char) lport; + break; + } +#endif + default: { + *buf_ptr++ = 0x00; + *buf_ptr++ = 0x00; + *buf_ptr++ = 0x00; + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "unsupported address family"); + return NULL; + } + } + + if (c->ppv2_tlv.len) { + buf_ptr = ngx_cpymem(buf_ptr, + c->ppv2_tlv.pos, + c->ppv2_tlv.len); + } + + return buf_ptr; +} + static u_char * ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf, u_char *last) diff --git a/src/core/ngx_proxy_protocol.h b/src/core/ngx_proxy_protocol.h index d1749f57b..1db1f2b2e 100644 --- a/src/core/ngx_proxy_protocol.h +++ b/src/core/ngx_proxy_protocol.h @@ -4,34 +4,46 @@ * Copyright (C) Nginx, Inc. */ - #ifndef _NGX_PROXY_PROTOCOL_H_INCLUDED_ #define _NGX_PROXY_PROTOCOL_H_INCLUDED_ - +#include "ngx_string.h" +#include #include #include - -#define NGX_PROXY_PROTOCOL_V1_MAX_HEADER 107 -#define NGX_PROXY_PROTOCOL_MAX_HEADER 4096 - +#define NGX_PROXY_PROTOCOL_V1_MAX_HEADER 107 +#define NGX_PROXY_PROTOCOL_V2_MAX_HEADER 64 +#define NGX_PROXY_PROTOCOL_MAX_HEADER 4096 struct ngx_proxy_protocol_s { - ngx_str_t src_addr; - ngx_str_t dst_addr; - in_port_t src_port; - in_port_t dst_port; - ngx_str_t tlvs; + ngx_str_t src_addr; + ngx_str_t dst_addr; + in_port_t src_port; + in_port_t dst_port; + ngx_str_t tlvs; }; +typedef struct { + u_char *pos; + u_char len; +} ngx_proxy_protocol_tlv_buf_t; + +typedef struct { + uint8_t type; + uint16_t length; + ngx_str_t value; +} ngx_proxy_v2_tlv; -u_char *ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, - u_char *last); +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); +u_char *ngx_proxy_protocol_v2_write(ngx_connection_t *c, u_char *buf, + u_char *last); +ngx_int_t ngx_proxy_protocol_v2_build_tlv(ngx_connection_t *c, ngx_proxy_v2_tlv *tlvs, + ngx_uint_t n); ngx_int_t ngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name, - ngx_str_t *value); + ngx_str_t *value); #endif /* _NGX_PROXY_PROTOCOL_H_INCLUDED_ */ diff --git a/src/stream/ngx_stream_proxy_module.c b/src/stream/ngx_stream_proxy_module.c index 300bdf681..d129816d6 100644 --- a/src/stream/ngx_stream_proxy_module.c +++ b/src/stream/ngx_stream_proxy_module.c @@ -9,7 +9,6 @@ #include #include - typedef struct { ngx_addr_t *addr; ngx_stream_complex_value_t *value; @@ -31,9 +30,11 @@ typedef struct { ngx_uint_t next_upstream_tries; ngx_flag_t next_upstream; ngx_flag_t proxy_protocol; + ngx_flag_t protocol_version; ngx_flag_t half_close; ngx_stream_upstream_local_t *local; ngx_flag_t socket_keepalive; + ngx_array_t *pp2_tlvs; #if (NGX_STREAM_SSL) ngx_flag_t ssl_enable; @@ -91,6 +92,8 @@ static char *ngx_stream_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_stream_proxy_bind(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_stream_proxy_protocol_tlv(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); #if (NGX_STREAM_SSL) @@ -253,12 +256,26 @@ static ngx_command_t ngx_stream_proxy_commands[] = { offsetof(ngx_stream_proxy_srv_conf_t, proxy_protocol), NULL }, + { ngx_string("proxy_protocol_version"), + NGX_STREAM_MAIN_CONF | NGX_STREAM_SRV_CONF | NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, protocol_version), + NULL}, + { ngx_string("proxy_half_close"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_proxy_srv_conf_t, half_close), NULL }, + + { ngx_string("proxy_protocol_tlv"), + NGX_STREAM_SRV_CONF | NGX_CONF_TAKE2, + ngx_stream_proxy_protocol_tlv, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, #if (NGX_STREAM_SSL) @@ -400,6 +417,46 @@ ngx_module_t ngx_stream_proxy_module = { NGX_MODULE_V1_PADDING }; +static char * +ngx_stream_proxy_protocol_tlv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_proxy_srv_conf_t *pscf = conf; + ngx_proxy_v2_tlv *tlv; + ngx_str_t *value; + + if (pscf->pp2_tlvs == NULL) { + pscf->pp2_tlvs = ngx_array_create(cf->pool, 2, + sizeof(ngx_proxy_v2_tlv)); + if (pscf->pp2_tlvs == NULL) { + return NGX_CONF_ERROR; + } + } + + tlv = ngx_array_push(pscf->pp2_tlvs); + if (tlv == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + if (value[1].len > 2 + && value[1].data[0] == '0' + && value[1].data[1] == 'x') + { + tlv->type = (uint8_t) ngx_hextoi(value[1].data + 2, + value[1].len - 2); + } else if (ngx_strcmp(value[1].data, "alpn") == 0) { + tlv->type = 0x01; + } else { + return "unknown proxy protocol v2 TLV type"; + } + + tlv->value = value[2]; + tlv->length = (uint16_t) value[2].len; + + return NGX_CONF_OK; +} + static void ngx_stream_proxy_handler(ngx_stream_session_t *s) @@ -834,6 +891,15 @@ ngx_stream_proxy_init_upstream(ngx_stream_session_t *s) pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); + if (pscf->pp2_tlvs && pscf->pp2_tlvs->nelts) { + ngx_proxy_protocol_v2_build_tlv( + s->connection, + pscf->pp2_tlvs->elts, + pscf->pp2_tlvs->nelts + ); + } + + #if (NGX_STREAM_SSL) if (pc->type == SOCK_STREAM && pscf->ssl_enable) { @@ -927,7 +993,11 @@ ngx_stream_proxy_init_upstream(ngx_stream_session_t *s) return; } - p = ngx_pnalloc(c->pool, NGX_PROXY_PROTOCOL_V1_MAX_HEADER); + if (pscf->proxy_protocol == 2){ + p = ngx_pnalloc(c->pool, NGX_PROXY_PROTOCOL_V2_MAX_HEADER); + } else{ + p = ngx_pnalloc(c->pool, NGX_PROXY_PROTOCOL_V1_MAX_HEADER); + } if (p == NULL) { ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; @@ -935,8 +1005,13 @@ 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); + if (pscf->protocol_version == 2) { + p = ngx_proxy_protocol_v2_write(c, p, + p + NGX_PROXY_PROTOCOL_V2_MAX_HEADER); + } else { + p = ngx_proxy_protocol_write(c, p, + p + NGX_PROXY_PROTOCOL_V1_MAX_HEADER); + } if (p == NULL) { ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; @@ -969,7 +1044,6 @@ ngx_stream_proxy_init_upstream(ngx_stream_session_t *s) ngx_stream_proxy_process(s, 0, 1); } - #if (NGX_STREAM_SSL) static ngx_int_t @@ -986,9 +1060,16 @@ 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"); + + pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); - p = ngx_proxy_protocol_write(c, buf, - buf + NGX_PROXY_PROTOCOL_V1_MAX_HEADER); + if (pscf->protocol_version == 2) { + p = ngx_proxy_protocol_v2_write(c, buf, + buf + NGX_PROXY_PROTOCOL_V2_MAX_HEADER); + } else { + p = ngx_proxy_protocol_write(c, buf, + buf + NGX_PROXY_PROTOCOL_V1_MAX_HEADER); + if (p == NULL) { ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return NGX_ERROR; @@ -2216,9 +2297,11 @@ ngx_stream_proxy_create_srv_conf(ngx_conf_t *cf) conf->next_upstream_tries = NGX_CONF_UNSET_UINT; conf->next_upstream = NGX_CONF_UNSET; conf->proxy_protocol = NGX_CONF_UNSET; + conf->protocol_version = NGX_CONF_UNSET_UINT; conf->local = NGX_CONF_UNSET_PTR; conf->socket_keepalive = NGX_CONF_UNSET; conf->half_close = NGX_CONF_UNSET; + conf->pp2_tlvs = NULL; #if (NGX_STREAM_SSL) conf->ssl_enable = NGX_CONF_UNSET; @@ -2243,6 +2326,10 @@ ngx_stream_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_stream_proxy_srv_conf_t *prev = parent; ngx_stream_proxy_srv_conf_t *conf = child; + + if (conf->pp2_tlvs == NULL) { + conf->pp2_tlvs = prev->pp2_tlvs; + } ngx_conf_merge_msec_value(conf->connect_timeout, prev->connect_timeout, 60000);