IPv6 & IPv4 backend with proxy_bind

Ruslan Ermilov ru at nginx.com
Mon Dec 9 10:29:29 UTC 2013


On Sat, Nov 23, 2013 at 12:15:28PM +1030, SplitIce wrote:
> Attached is the patch,
> 
> This is the first time I have created a variable or really done anything
> inside the http request processing flow so feel free to let me know if
> there is a better way to do something or if I have any edge cases.
> 
> This patch provides a $upstream_connecting variable which contains the IP
> address and port of the upstream being connected. If there is no upstream,
> it will return "-" my understanding is this may happen if the upstream is
> DNS resolved (untested). There may be a better way of doing this?
> 
> This should be used in a config like the following -
>         map $upstream_connecting $test {
> ~^93\.184\.216\.119\: 192.168.2.40;
> ~^192\.168\.2\.([0-9]+)\: 192.168.2.40;
> }
> 
>         proxy_bind $test;

I took a different approach.  I've made "local" a peer's method,
so the computation of the local address is delayed until it's
actually needed.  By that time, the peer address is already known.

I've also patched "map" so it creates non-cacheable variables.

This way, $upstream_peer_addr can be mapped into the local address:

map $upstream_peer_addr $bind_addr {
    127.0.0.1:8001 127.0.0.1;
    127.0.0.1:8002 127.0.0.1;
    [::1]:8003     ::1;
}

server {
    ...
    location / {
        proxy_pass ...;
	proxy_bind $bind_addr;
	...
    }
}

diff --git a/src/event/ngx_event_connect.c b/src/event/ngx_event_connect.c
--- a/src/event/ngx_event_connect.c
+++ b/src/event/ngx_event_connect.c
@@ -17,6 +17,7 @@ ngx_event_connect_peer(ngx_peer_connecti
     int                rc;
     ngx_int_t          event;
     ngx_err_t          err;
+    ngx_addr_t        *local;
     ngx_uint_t         level;
     ngx_socket_t       s;
     ngx_event_t       *rev, *wev;
@@ -67,9 +68,13 @@ ngx_event_connect_peer(ngx_peer_connecti
     }
 
     if (pc->local) {
-        if (bind(s, pc->local->sockaddr, pc->local->socklen) == -1) {
+        local = pc->local(pc, pc->data);
+
+        if (local != NULL
+            && bind(s, local->sockaddr, local->socklen) == -1)
+        {
             ngx_log_error(NGX_LOG_CRIT, pc->log, ngx_socket_errno,
-                          "bind(%V) failed", &pc->local->name);
+                          "bind(%V) failed", &local->name);
 
             goto failed;
         }
diff --git a/src/event/ngx_event_connect.h b/src/event/ngx_event_connect.h
--- a/src/event/ngx_event_connect.h
+++ b/src/event/ngx_event_connect.h
@@ -25,6 +25,8 @@ typedef ngx_int_t (*ngx_event_get_peer_p
     void *data);
 typedef void (*ngx_event_free_peer_pt)(ngx_peer_connection_t *pc, void *data,
     ngx_uint_t state);
+typedef ngx_addr_t *(*ngx_event_get_local_pt)(ngx_peer_connection_t *pc,
+    void *data);
 #if (NGX_SSL)
 
 typedef ngx_int_t (*ngx_event_set_peer_session_pt)(ngx_peer_connection_t *pc,
@@ -45,6 +47,7 @@ struct ngx_peer_connection_s {
 
     ngx_event_get_peer_pt            get;
     ngx_event_free_peer_pt           free;
+    ngx_event_get_local_pt           local;
     void                            *data;
 
 #if (NGX_SSL)
@@ -56,8 +59,6 @@ struct ngx_peer_connection_s {
     ngx_atomic_t                    *lock;
 #endif
 
-    ngx_addr_t                      *local;
-
     int                              rcvbuf;
 
     ngx_log_t                       *log;
diff --git a/src/http/modules/ngx_http_map_module.c b/src/http/modules/ngx_http_map_module.c
--- a/src/http/modules/ngx_http_map_module.c
+++ b/src/http/modules/ngx_http_map_module.c
@@ -477,7 +477,7 @@ ngx_http_map(ngx_conf_t *cf, ngx_command
     }
 
     var->valid = 1;
-    var->no_cacheable = 0;
+    var->no_cacheable = 1;
     var->not_found = 0;
 
     vp = ngx_array_push(&ctx->values_hash[key]);
diff --git a/src/http/modules/ngx_http_upstream_keepalive_module.c b/src/http/modules/ngx_http_upstream_keepalive_module.c
--- a/src/http/modules/ngx_http_upstream_keepalive_module.c
+++ b/src/http/modules/ngx_http_upstream_keepalive_module.c
@@ -31,6 +31,7 @@ typedef struct {
 
     ngx_event_get_peer_pt              original_get_peer;
     ngx_event_free_peer_pt             original_free_peer;
+    ngx_event_get_local_pt             original_get_local;
 
 #if (NGX_HTTP_SSL)
     ngx_event_set_peer_session_pt      original_set_session;
@@ -63,6 +64,9 @@ static void ngx_http_upstream_keepalive_
 static void ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev);
 static void ngx_http_upstream_keepalive_close(ngx_connection_t *c);
 
+static ngx_addr_t *ngx_http_upstream_get_keepalive_local(
+    ngx_peer_connection_t *pc, void *data);
+
 
 #if (NGX_HTTP_SSL)
 static ngx_int_t ngx_http_upstream_keepalive_set_session(
@@ -189,10 +193,12 @@ ngx_http_upstream_init_keepalive_peer(ng
     kp->data = r->upstream->peer.data;
     kp->original_get_peer = r->upstream->peer.get;
     kp->original_free_peer = r->upstream->peer.free;
+    kp->original_get_local = r->upstream->peer.local;
 
     r->upstream->peer.data = kp;
     r->upstream->peer.get = ngx_http_upstream_get_keepalive_peer;
     r->upstream->peer.free = ngx_http_upstream_free_keepalive_peer;
+    r->upstream->peer.local = ngx_http_upstream_get_keepalive_local;
 
 #if (NGX_HTTP_SSL)
     kp->original_set_session = r->upstream->peer.set_session;
@@ -430,6 +436,16 @@ ngx_http_upstream_keepalive_close(ngx_co
 }
 
 
+static ngx_addr_t *
+ngx_http_upstream_get_keepalive_local(ngx_peer_connection_t *pc,
+    void *data)
+{
+    ngx_http_upstream_keepalive_peer_data_t  *kp = data;
+
+    return kp->original_get_local(pc, kp->data);
+}
+
+
 #if (NGX_HTTP_SSL)
 
 static ngx_int_t
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -141,14 +141,13 @@ static ngx_int_t ngx_http_upstream_respo
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_upstream_response_length_variable(
     ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upstream_peer_addr_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 
 static char *ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy);
 static char *ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 
-static ngx_addr_t *ngx_http_upstream_get_local(ngx_http_request_t *r,
-    ngx_http_upstream_local_t *local);
-
 static void *ngx_http_upstream_create_main_conf(ngx_conf_t *cf);
 static char *ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf);
 
@@ -366,6 +365,10 @@ static ngx_http_variable_t  ngx_http_ups
 
 #endif
 
+    { ngx_string("upstream_peer_addr"), NULL,
+      ngx_http_upstream_peer_addr_variable, 0,
+      NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
     { ngx_null_string, NULL, NULL, 0, 0, 0 }
 };
 
@@ -534,8 +537,6 @@ ngx_http_upstream_init_request(ngx_http_
         return;
     }
 
-    u->peer.local = ngx_http_upstream_get_local(r, u->conf->local);
-
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
     u->output.alignment = clcf->directio_alignment;
@@ -4509,6 +4510,25 @@ ngx_http_upstream_response_length_variab
 }
 
 
+static ngx_int_t
+ngx_http_upstream_peer_addr_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    if (r->upstream == NULL || r->upstream->peer.name == NULL) {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    v->len = r->upstream->peer.name->len;
+    v->data = r->upstream->peer.name->data;
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+
+    return NGX_OK;
+}
+
+
 ngx_int_t
 ngx_http_upstream_header_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
@@ -5022,53 +5042,6 @@ ngx_http_upstream_bind_set_slot(ngx_conf
 }
 
 
-static ngx_addr_t *
-ngx_http_upstream_get_local(ngx_http_request_t *r,
-    ngx_http_upstream_local_t *local)
-{
-    ngx_int_t    rc;
-    ngx_str_t    val;
-    ngx_addr_t  *addr;
-
-    if (local == NULL) {
-        return NULL;
-    }
-
-    if (local->value == NULL) {
-        return local->addr;
-    }
-
-    if (ngx_http_complex_value(r, local->value, &val) != NGX_OK) {
-        return NULL;
-    }
-
-    if (val.len == 0) {
-        return NULL;
-    }
-
-    addr = ngx_palloc(r->pool, sizeof(ngx_addr_t));
-    if (addr == NULL) {
-        return NULL;
-    }
-
-    rc = ngx_parse_addr(r->pool, addr, val.data, val.len);
-
-    switch (rc) {
-    case NGX_OK:
-        addr->name = val;
-        return addr;
-
-    case NGX_DECLINED:
-        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                      "invalid local address \"%V\"", &val);
-        /* fall through */
-
-    default:
-        return NULL;
-    }
-}
-
-
 char *
 ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf)
diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c
--- a/src/http/ngx_http_upstream_round_robin.c
+++ b/src/http/ngx_http_upstream_round_robin.c
@@ -22,6 +22,9 @@ static void ngx_http_upstream_empty_save
 
 #endif
 
+static ngx_addr_t *ngx_http_upstream_get_local(ngx_peer_connection_t *pc,
+    void *data);
+
 
 ngx_int_t
 ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
@@ -246,6 +249,8 @@ ngx_http_upstream_init_round_robin_peer(
         }
     }
 
+    rrp->request = r;
+
     r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer;
     r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer;
     r->upstream->peer.tries = rrp->peers->number;
@@ -255,6 +260,7 @@ ngx_http_upstream_init_round_robin_peer(
     r->upstream->peer.save_session =
                                ngx_http_upstream_save_round_robin_peer_session;
 #endif
+    r->upstream->peer.local = ngx_http_upstream_get_local;
 
     return NGX_OK;
 }
@@ -679,3 +685,56 @@ ngx_http_upstream_empty_save_session(ngx
 }
 
 #endif
+
+
+static ngx_addr_t *
+ngx_http_upstream_get_local(ngx_peer_connection_t *pc, void *data)
+{
+    ngx_http_upstream_rr_peer_data_t  *rrp = data;
+
+    ngx_int_t                   rc;
+    ngx_str_t                   val;
+    ngx_addr_t                 *addr;
+    ngx_http_request_t         *r;
+    ngx_http_upstream_local_t  *local;
+
+    r = rrp->request;
+    local = r->upstream->conf->local;
+
+    if (local == NULL) {
+        return NULL;
+    }
+
+    if (local->value == NULL) {
+        return local->addr;
+    }
+
+    if (ngx_http_complex_value(r, local->value, &val) != NGX_OK) {
+        return NULL;
+    }
+
+    if (val.len == 0) {
+        return NULL;
+    }
+
+    addr = ngx_palloc(r->pool, sizeof(ngx_addr_t));
+    if (addr == NULL) {
+        return NULL;
+    }
+
+    rc = ngx_parse_addr(r->pool, addr, val.data, val.len);
+
+    switch (rc) {
+    case NGX_OK:
+        addr->name = val;
+        return addr;
+
+    case NGX_DECLINED:
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "invalid local address \"%V\"", &val);
+        /* fall through */
+
+    default:
+        return NULL;
+    }
+}
diff --git a/src/http/ngx_http_upstream_round_robin.h b/src/http/ngx_http_upstream_round_robin.h
--- a/src/http/ngx_http_upstream_round_robin.h
+++ b/src/http/ngx_http_upstream_round_robin.h
@@ -63,6 +63,7 @@ typedef struct {
     ngx_uint_t                      current;
     uintptr_t                      *tried;
     uintptr_t                       data;
+    ngx_http_request_t             *request;
 } ngx_http_upstream_rr_peer_data_t;
 
 



More information about the nginx-devel mailing list