[nginx] Stream: client-side PROXY protocol.

Roman Arutyunyan arut at nginx.com
Tue Jun 16 10:47:33 UTC 2015


details:   http://hg.nginx.org/nginx/rev/fa663739e115
branches:  
changeset: 6184:fa663739e115
user:      Roman Arutyunyan <arut at nginx.com>
date:      Tue Jun 16 13:45:16 2015 +0300
description:
Stream: client-side PROXY protocol.

The new directive "proxy_protocol" toggles sending out PROXY protocol header
to upstream once connection is established.

diffstat:

 src/core/ngx_proxy_protocol.c        |   49 ++++++++++++++
 src/core/ngx_proxy_protocol.h        |    2 +
 src/stream/ngx_stream_proxy_module.c |  119 ++++++++++++++++++++++++++++++++++-
 src/stream/ngx_stream_upstream.h     |    2 +
 4 files changed, 170 insertions(+), 2 deletions(-)

diffs (267 lines):

diff -r 4dcffe43a7ea -r fa663739e115 src/core/ngx_proxy_protocol.c
--- a/src/core/ngx_proxy_protocol.c	Tue Jun 16 09:02:45 2015 +0300
+++ b/src/core/ngx_proxy_protocol.c	Tue Jun 16 13:45:16 2015 +0300
@@ -89,3 +89,52 @@ invalid:
 
     return NULL;
 }
+
+
+u_char *
+ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last)
+{
+    ngx_uint_t  port, lport;
+
+    if (last - buf < NGX_PROXY_PROTOCOL_MAX_HEADER) {
+        return NULL;
+    }
+
+    if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
+        return NULL;
+    }
+
+    switch (c->sockaddr->sa_family) {
+
+    case AF_INET:
+        buf = ngx_cpymem(buf, "PROXY TCP4 ", sizeof("PROXY TCP4 ") - 1);
+
+        port = ntohs(((struct sockaddr_in *) c->sockaddr)->sin_port);
+        lport = ntohs(((struct sockaddr_in *) c->local_sockaddr)->sin_port);
+
+        break;
+
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+        buf = ngx_cpymem(buf, "PROXY TCP6 ", sizeof("PROXY TCP6 ") - 1);
+
+        port = ntohs(((struct sockaddr_in6 *) c->sockaddr)->sin6_port);
+        lport = ntohs(((struct sockaddr_in6 *) c->local_sockaddr)->sin6_port);
+
+        break;
+#endif
+
+    default:
+        return ngx_cpymem(buf, "PROXY UNKNOWN" CRLF,
+                          sizeof("PROXY UNKNOWN" CRLF) - 1);
+    }
+
+    buf += ngx_sock_ntop(c->sockaddr, c->socklen, buf, last - buf, 0);
+
+    *buf++ = ' ';
+
+    buf += ngx_sock_ntop(c->local_sockaddr, c->local_socklen, buf, last - buf,
+                         0);
+
+    return ngx_slprintf(buf, last, " %ui %ui" CRLF, port, lport);
+}
diff -r 4dcffe43a7ea -r fa663739e115 src/core/ngx_proxy_protocol.h
--- a/src/core/ngx_proxy_protocol.h	Tue Jun 16 09:02:45 2015 +0300
+++ b/src/core/ngx_proxy_protocol.h	Tue Jun 16 13:45:16 2015 +0300
@@ -18,6 +18,8 @@
 
 u_char *ngx_proxy_protocol_parse(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);
 
 
 #endif /* _NGX_PROXY_PROTOCOL_H_INCLUDED_ */
diff -r 4dcffe43a7ea -r fa663739e115 src/stream/ngx_stream_proxy_module.c
--- a/src/stream/ngx_stream_proxy_module.c	Tue Jun 16 09:02:45 2015 +0300
+++ b/src/stream/ngx_stream_proxy_module.c	Tue Jun 16 13:45:16 2015 +0300
@@ -21,6 +21,7 @@ typedef struct {
     size_t                           upstream_buf_size;
     ngx_uint_t                       next_upstream_tries;
     ngx_flag_t                       next_upstream;
+    ngx_flag_t                       proxy_protocol;
     ngx_addr_t                      *local;
 
 #if (NGX_STREAM_SSL)
@@ -67,6 +68,7 @@ static char *ngx_stream_proxy_pass(ngx_c
     void *conf);
 static char *ngx_stream_proxy_bind(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static ngx_int_t ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s);
 
 #if (NGX_STREAM_SSL)
 
@@ -156,6 +158,13 @@ static ngx_command_t  ngx_stream_proxy_c
       offsetof(ngx_stream_proxy_srv_conf_t, next_upstream_timeout),
       NULL },
 
+    { ngx_string("proxy_protocol"),
+      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, proxy_protocol),
+      NULL },
+
 #if (NGX_STREAM_SSL)
 
     { ngx_string("proxy_ssl"),
@@ -328,6 +337,8 @@ ngx_stream_proxy_handler(ngx_stream_sess
         u->peer.tries = pscf->next_upstream_tries;
     }
 
+    u->proxy_protocol = pscf->proxy_protocol;
+
     p = ngx_pnalloc(c->pool, pscf->downstream_buf_size);
     if (p == NULL) {
         ngx_stream_proxy_finalize(s, NGX_ERROR);
@@ -342,6 +353,29 @@ ngx_stream_proxy_handler(ngx_stream_sess
     c->write->handler = ngx_stream_proxy_downstream_handler;
     c->read->handler = ngx_stream_proxy_downstream_handler;
 
+    if (u->proxy_protocol
+#if (NGX_STREAM_SSL)
+        && pscf->ssl == NULL
+#endif
+        && pscf->downstream_buf_size >= NGX_PROXY_PROTOCOL_MAX_HEADER
+       )
+    {
+        /* optimization for a typical case */
+
+        ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
+                       "stream proxy send PROXY protocol header");
+
+        p = ngx_proxy_protocol_write(c, u->downstream_buf.last,
+                                     u->downstream_buf.end);
+        if (p == NULL) {
+            ngx_stream_proxy_finalize(s, NGX_ERROR);
+            return;
+        }
+
+        u->downstream_buf.last = p;
+        u->proxy_protocol = 0;
+    }
+
     if (ngx_stream_proxy_process(s, 0, 0) != NGX_OK) {
         return;
     }
@@ -417,10 +451,18 @@ ngx_stream_proxy_init_upstream(ngx_strea
     ngx_stream_upstream_t        *u;
     ngx_stream_proxy_srv_conf_t  *pscf;
 
+    u = s->upstream;
+
+    if (u->proxy_protocol) {
+        if (ngx_stream_proxy_send_proxy_protocol(s) != NGX_OK) {
+            return;
+        }
+
+        u->proxy_protocol = 0;
+    }
+
     pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);
 
-    u = s->upstream;
-
     pc = u->peer.connection;
 
 #if (NGX_STREAM_SSL)
@@ -474,6 +516,76 @@ ngx_stream_proxy_init_upstream(ngx_strea
 }
 
 
+static ngx_int_t
+ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s)
+{
+    u_char                       *p;
+    ssize_t                       n, size;
+    ngx_connection_t             *c, *pc;
+    ngx_stream_upstream_t        *u;
+    ngx_stream_proxy_srv_conf_t  *pscf;
+    u_char                        buf[NGX_PROXY_PROTOCOL_MAX_HEADER];
+
+    c = s->connection;
+
+    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_MAX_HEADER);
+    if (p == NULL) {
+        ngx_stream_proxy_finalize(s, NGX_ERROR);
+        return NGX_ERROR;
+    }
+
+    u = s->upstream;
+
+    pc = u->peer.connection;
+
+    size = p - buf;
+
+    n = pc->send(pc, buf, size);
+
+    if (n == NGX_AGAIN) {
+        if (ngx_handle_write_event(pc->write, 0) != NGX_OK) {
+            ngx_stream_proxy_finalize(s, NGX_ERROR);
+            return NGX_ERROR;
+        }
+
+        pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);
+
+        ngx_add_timer(pc->write, pscf->timeout);
+
+        pc->write->handler = ngx_stream_proxy_connect_handler;
+
+        return NGX_AGAIN;
+    }
+
+    if (n == NGX_ERROR) {
+        ngx_stream_proxy_finalize(s, NGX_DECLINED);
+        return NGX_ERROR;
+    }
+
+    if (n != size) {
+
+        /*
+         * PROXY protocol specification:
+         * The sender must always ensure that the header
+         * is sent at once, so that the transport layer
+         * maintains atomicity along the path to the receiver.
+         */
+
+        ngx_log_error(NGX_LOG_ERR, c->log, 0,
+                      "could not send PROXY protocol header at once");
+
+        ngx_stream_proxy_finalize(s, NGX_DECLINED);
+
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
 #if (NGX_STREAM_SSL)
 
 static char *
@@ -1105,6 +1217,7 @@ ngx_stream_proxy_create_srv_conf(ngx_con
     conf->upstream_buf_size = NGX_CONF_UNSET_SIZE;
     conf->next_upstream_tries = NGX_CONF_UNSET_UINT;
     conf->next_upstream = NGX_CONF_UNSET;
+    conf->proxy_protocol = NGX_CONF_UNSET;
     conf->local = NGX_CONF_UNSET_PTR;
 
 #if (NGX_STREAM_SSL)
@@ -1146,6 +1259,8 @@ ngx_stream_proxy_merge_srv_conf(ngx_conf
 
     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_ptr_value(conf->local, prev->local, NULL);
 
 #if (NGX_STREAM_SSL)
diff -r 4dcffe43a7ea -r fa663739e115 src/stream/ngx_stream_upstream.h
--- a/src/stream/ngx_stream_upstream.h	Tue Jun 16 09:02:45 2015 +0300
+++ b/src/stream/ngx_stream_upstream.h	Tue Jun 16 13:45:16 2015 +0300
@@ -86,6 +86,8 @@ typedef struct {
 #if (NGX_STREAM_SSL)
     ngx_str_t                          ssl_name;
 #endif
+    ngx_uint_t                         proxy_protocol;
+                                               /* unsigned  proxy_protocol:1; */
 } ngx_stream_upstream_t;
 
 



More information about the nginx-devel mailing list