[nginx] svn commit: r4993 - in trunk/src: core http/modules

ru at nginx.com ru at nginx.com
Tue Dec 25 08:21:56 UTC 2012


Author: ru
Date: 2012-12-25 08:21:56 +0000 (Tue, 25 Dec 2012)
New Revision: 4993
URL: http://trac.nginx.org/nginx/changeset/4993/nginx

Log:
Geo: IPv6 support.

The "ranges" mode is still limited to IPv4 only.


Modified:
   trunk/src/core/ngx_radix_tree.c
   trunk/src/core/ngx_radix_tree.h
   trunk/src/http/modules/ngx_http_geo_module.c

Modified: trunk/src/core/ngx_radix_tree.c
===================================================================
--- trunk/src/core/ngx_radix_tree.c	2012-12-25 08:02:21 UTC (rev 4992)
+++ trunk/src/core/ngx_radix_tree.c	2012-12-25 08:21:56 UTC (rev 4993)
@@ -263,6 +263,203 @@
 }
 
 
+#if (NGX_HAVE_INET6)
+
+ngx_int_t
+ngx_radix128tree_insert(ngx_radix_tree_t *tree, u_char *key, u_char *mask,
+    uintptr_t value)
+{
+    u_char             bit;
+    ngx_uint_t         i;
+    ngx_radix_node_t  *node, *next;
+
+    i = 0;
+    bit = 0x80;
+
+    node = tree->root;
+    next = tree->root;
+
+    while (bit & mask[i]) {
+        if (key[i] & bit) {
+            next = node->right;
+
+        } else {
+            next = node->left;
+        }
+
+        if (next == NULL) {
+            break;
+        }
+
+        bit >>= 1;
+        node = next;
+
+        if (bit == 0) {
+            if (++i == 16) {
+                break;
+            }
+
+            bit = 0x80;
+        }
+    }
+
+    if (next) {
+        if (node->value != NGX_RADIX_NO_VALUE) {
+            return NGX_BUSY;
+        }
+
+        node->value = value;
+        return NGX_OK;
+    }
+
+    while (bit & mask[i]) {
+        next = ngx_radix_alloc(tree);
+        if (next == NULL) {
+            return NGX_ERROR;
+        }
+
+        next->right = NULL;
+        next->left = NULL;
+        next->parent = node;
+        next->value = NGX_RADIX_NO_VALUE;
+
+        if (key[i] & bit) {
+            node->right = next;
+
+        } else {
+            node->left = next;
+        }
+
+        bit >>= 1;
+        node = next;
+
+        if (bit == 0) {
+            if (++i == 16) {
+                break;
+            }
+
+            bit = 0x80;
+        }
+    }
+
+    node->value = value;
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_radix128tree_delete(ngx_radix_tree_t *tree, u_char *key, u_char *mask)
+{
+    u_char             bit;
+    ngx_uint_t         i;
+    ngx_radix_node_t  *node;
+
+    i = 0;
+    bit = 0x80;
+    node = tree->root;
+
+    while (node && (bit & mask[i])) {
+        if (key[i] & bit) {
+            node = node->right;
+
+        } else {
+            node = node->left;
+        }
+
+        bit >>= 1;
+
+        if (bit == 0) {
+            if (++i == 16) {
+                break;
+            }
+
+            bit = 0x80;
+        }
+    }
+
+    if (node == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (node->right || node->left) {
+        if (node->value != NGX_RADIX_NO_VALUE) {
+            node->value = NGX_RADIX_NO_VALUE;
+            return NGX_OK;
+        }
+
+        return NGX_ERROR;
+    }
+
+    for ( ;; ) {
+        if (node->parent->right == node) {
+            node->parent->right = NULL;
+
+        } else {
+            node->parent->left = NULL;
+        }
+
+        node->right = tree->free;
+        tree->free = node;
+
+        node = node->parent;
+
+        if (node->right || node->left) {
+            break;
+        }
+
+        if (node->value != NGX_RADIX_NO_VALUE) {
+            break;
+        }
+
+        if (node->parent == NULL) {
+            break;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+uintptr_t
+ngx_radix128tree_find(ngx_radix_tree_t *tree, u_char *key)
+{
+    u_char             bit;
+    uintptr_t          value;
+    ngx_uint_t         i;
+    ngx_radix_node_t  *node;
+
+    i = 0;
+    bit = 0x80;
+    value = NGX_RADIX_NO_VALUE;
+    node = tree->root;
+
+    while (node) {
+        if (node->value != NGX_RADIX_NO_VALUE) {
+            value = node->value;
+        }
+
+        if (key[i] & bit) {
+            node = node->right;
+
+        } else {
+            node = node->left;
+        }
+
+        bit >>= 1;
+
+        if (bit == 0) {
+            i++;
+            bit = 0x80;
+        }
+    }
+
+    return value;
+}
+
+#endif
+
+
 static ngx_radix_node_t *
 ngx_radix_alloc(ngx_radix_tree_t *tree)
 {

Modified: trunk/src/core/ngx_radix_tree.h
===================================================================
--- trunk/src/core/ngx_radix_tree.h	2012-12-25 08:02:21 UTC (rev 4992)
+++ trunk/src/core/ngx_radix_tree.h	2012-12-25 08:21:56 UTC (rev 4993)
@@ -36,11 +36,20 @@
 
 ngx_radix_tree_t *ngx_radix_tree_create(ngx_pool_t *pool,
     ngx_int_t preallocate);
+
 ngx_int_t ngx_radix32tree_insert(ngx_radix_tree_t *tree,
     uint32_t key, uint32_t mask, uintptr_t value);
 ngx_int_t ngx_radix32tree_delete(ngx_radix_tree_t *tree,
     uint32_t key, uint32_t mask);
 uintptr_t ngx_radix32tree_find(ngx_radix_tree_t *tree, uint32_t key);
 
+#if (NGX_HAVE_INET6)
+ngx_int_t ngx_radix128tree_insert(ngx_radix_tree_t *tree,
+    u_char *key, u_char *mask, uintptr_t value);
+ngx_int_t ngx_radix128tree_delete(ngx_radix_tree_t *tree,
+    u_char *key, u_char *mask);
+uintptr_t ngx_radix128tree_find(ngx_radix_tree_t *tree, u_char *key);
+#endif
 
+
 #endif /* _NGX_RADIX_TREE_H_INCLUDED_ */

Modified: trunk/src/http/modules/ngx_http_geo_module.c
===================================================================
--- trunk/src/http/modules/ngx_http_geo_module.c	2012-12-25 08:02:21 UTC (rev 4992)
+++ trunk/src/http/modules/ngx_http_geo_module.c	2012-12-25 08:21:56 UTC (rev 4993)
@@ -18,6 +18,14 @@
 
 
 typedef struct {
+    ngx_radix_tree_t                *tree;
+#if (NGX_HAVE_INET6)
+    ngx_radix_tree_t                *tree6;
+#endif
+} ngx_http_geo_trees_t;
+
+
+typedef struct {
     ngx_http_geo_range_t           **low;
     ngx_http_variable_value_t       *default_value;
 } ngx_http_geo_high_ranges_t;
@@ -35,6 +43,9 @@
     ngx_str_t                       *net;
     ngx_http_geo_high_ranges_t       high;
     ngx_radix_tree_t                *tree;
+#if (NGX_HAVE_INET6)
+    ngx_radix_tree_t                *tree6;
+#endif
     ngx_rbtree_t                     rbtree;
     ngx_rbtree_node_t                sentinel;
     ngx_array_t                     *proxies;
@@ -57,7 +68,7 @@
 
 typedef struct {
     union {
-        ngx_radix_tree_t            *tree;
+        ngx_http_geo_trees_t         trees;
         ngx_http_geo_high_ranges_t   high;
     } u;
 
@@ -68,8 +79,8 @@
 } ngx_http_geo_ctx_t;
 
 
-static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r,
-    ngx_http_geo_ctx_t *ctx);
+static ngx_int_t ngx_http_geo_addr(ngx_http_request_t *r,
+    ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);
 static ngx_int_t ngx_http_geo_real_addr(ngx_http_request_t *r,
     ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);
 static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
@@ -155,7 +166,7 @@
 };
 
 
-/* AF_INET only */
+/* geo range is AF_INET only */
 
 static ngx_int_t
 ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
@@ -163,11 +174,57 @@
 {
     ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
 
+    in_addr_t                   inaddr;
+    ngx_addr_t                  addr;
+    struct sockaddr_in         *sin;
     ngx_http_variable_value_t  *vv;
+#if (NGX_HAVE_INET6)
+    u_char                     *p;
+    struct in6_addr            *inaddr6;
+#endif
 
-    vv = (ngx_http_variable_value_t *)
-              ngx_radix32tree_find(ctx->u.tree, ngx_http_geo_addr(r, ctx));
+    if (ngx_http_geo_addr(r, ctx, &addr) != NGX_OK) {
+        vv = (ngx_http_variable_value_t *)
+                  ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE);
+        goto done;
+    }
 
+    switch (addr.sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+        inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
+        p = inaddr6->s6_addr;
+
+        if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
+            inaddr = p[12] << 24;
+            inaddr += p[13] << 16;
+            inaddr += p[14] << 8;
+            inaddr += p[15];
+
+            vv = (ngx_http_variable_value_t *)
+                      ngx_radix32tree_find(ctx->u.trees.tree, inaddr);
+
+        } else {
+            vv = (ngx_http_variable_value_t *)
+                      ngx_radix128tree_find(ctx->u.trees.tree6, p);
+        }
+
+        break;
+#endif
+
+    default: /* AF_INET */
+        sin = (struct sockaddr_in *) addr.sockaddr;
+        inaddr = ntohl(sin->sin_addr.s_addr);
+
+        vv = (ngx_http_variable_value_t *)
+                  ngx_radix32tree_find(ctx->u.trees.tree, inaddr);
+
+        break;
+    }
+
+done:
+
     *v = *vv;
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -183,19 +240,56 @@
 {
     ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
 
-    in_addr_t              addr;
+    in_addr_t              inaddr;
+    ngx_addr_t             addr;
     ngx_uint_t             n;
+    struct sockaddr_in    *sin;
     ngx_http_geo_range_t  *range;
+#if (NGX_HAVE_INET6)
+    u_char                *p;
+    struct in6_addr       *inaddr6;
+#endif
 
     *v = *ctx->u.high.default_value;
 
+    if (ngx_http_geo_addr(r, ctx, &addr) == NGX_OK) {
+
+        switch (addr.sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+        case AF_INET6:
+            inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
+
+            if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
+                p = inaddr6->s6_addr;
+
+                inaddr = p[12] << 24;
+                inaddr += p[13] << 16;
+                inaddr += p[14] << 8;
+                inaddr += p[15];
+
+            } else {
+                inaddr = INADDR_NONE;
+            }
+
+            break;
+#endif
+
+        default: /* AF_INET */
+            sin = (struct sockaddr_in *) addr.sockaddr;
+            inaddr = ntohl(sin->sin_addr.s_addr);
+            break;
+        }
+
+    } else {
+        inaddr = INADDR_NONE;
+    }
+
     if (ctx->u.high.low) {
-        addr = ngx_http_geo_addr(r, ctx);
+        range = ctx->u.high.low[inaddr >> 16];
 
-        range = ctx->u.high.low[addr >> 16];
-
         if (range) {
-            n = addr & 0xffff;
+            n = inaddr & 0xffff;
             do {
                 if (n >= (ngx_uint_t) range->start
                     && n <= (ngx_uint_t) range->end)
@@ -214,54 +308,25 @@
 }
 
 
-static in_addr_t
-ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx)
+static ngx_int_t
+ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,
+    ngx_addr_t *addr)
 {
-    ngx_addr_t           addr;
-    ngx_table_elt_t     *xfwd;
-    struct sockaddr_in  *sin;
+    ngx_table_elt_t  *xfwd;
 
-    if (ngx_http_geo_real_addr(r, ctx, &addr) != NGX_OK) {
-        return INADDR_NONE;
+    if (ngx_http_geo_real_addr(r, ctx, addr) != NGX_OK) {
+        return NGX_ERROR;
     }
 
     xfwd = r->headers_in.x_forwarded_for;
 
     if (xfwd != NULL && ctx->proxies != NULL) {
-        (void) ngx_http_get_forwarded_addr(r, &addr, xfwd->value.data,
+        (void) ngx_http_get_forwarded_addr(r, addr, xfwd->value.data,
                                            xfwd->value.len, ctx->proxies,
                                            ctx->proxy_recursive);
     }
 
-#if (NGX_HAVE_INET6)
-
-    if (addr.sockaddr->sa_family == AF_INET6) {
-        u_char           *p;
-        in_addr_t         inaddr;
-        struct in6_addr  *inaddr6;
-
-        inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
-
-        if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
-            p = inaddr6->s6_addr;
-
-            inaddr = p[12] << 24;
-            inaddr += p[13] << 16;
-            inaddr += p[14] << 8;
-            inaddr += p[15];
-
-            return inaddr;
-        }
-    }
-
-#endif
-
-    if (addr.sockaddr->sa_family != AF_INET) {
-        return INADDR_NONE;
-    }
-
-    sin = (struct sockaddr_in *) addr.sockaddr;
-    return ntohl(sin->sin_addr.s_addr);
+    return NGX_OK;
 }
 
 
@@ -315,6 +380,9 @@
     ngx_http_variable_t      *var;
     ngx_http_geo_ctx_t       *geo;
     ngx_http_geo_conf_ctx_t   ctx;
+#if (NGX_HAVE_INET6)
+    static struct in6_addr    zero;
+#endif
 
     value = cf->args->elts;
 
@@ -445,8 +513,19 @@
             }
         }
 
-        geo->u.tree = ctx.tree;
+        geo->u.trees.tree = ctx.tree;
 
+#if (NGX_HAVE_INET6)
+        if (ctx.tree6 == NULL) {
+            ctx.tree6 = ngx_radix_tree_create(cf->pool, -1);
+            if (ctx.tree6 == NULL) {
+                return NGX_CONF_ERROR;
+            }
+        }
+
+        geo->u.trees.tree6 = ctx.tree6;
+#endif
+
         var->get_handler = ngx_http_geo_cidr_variable;
         var->data = (uintptr_t) geo;
 
@@ -461,6 +540,15 @@
         }
 
         /* NGX_BUSY is okay (default was set explicitly) */
+
+#if (NGX_HAVE_INET6)
+        if (ngx_radix128tree_insert(ctx.tree6, zero.s6_addr, zero.s6_addr,
+                                    (uintptr_t) &ngx_http_variable_null_value)
+            == NGX_ERROR)
+        {
+            return NGX_CONF_ERROR;
+        }
+#endif
     }
 
     return rv;
@@ -483,7 +571,12 @@
 
         if (ngx_strcmp(value[0].data, "ranges") == 0) {
 
-            if (ctx->tree) {
+            if (ctx->tree
+#if (NGX_HAVE_INET6)
+                || ctx->tree6
+#endif
+               )
+            {
                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                    "the \"ranges\" directive must be "
                                    "the first directive inside \"geo\" block");
@@ -934,6 +1027,15 @@
         }
     }
 
+#if (NGX_HAVE_INET6)
+    if (ctx->tree6 == NULL) {
+        ctx->tree6 = ngx_radix_tree_create(ctx->pool, -1);
+        if (ctx->tree6 == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+#endif
+
     if (ngx_strcmp(value[0].data, "default") == 0) {
         /* cidr.family = AF_INET; */
         cidr.u.in.addr = 0;
@@ -954,20 +1056,29 @@
             return NGX_CONF_ERROR;
         }
 
-        if (cidr.family != AF_INET) {
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "\"geo\" supports IPv4 only");
-            return NGX_CONF_ERROR;
+        if (cidr.family == AF_INET) {
+            cidr.u.in.addr = ntohl(cidr.u.in.addr);
+            cidr.u.in.mask = ntohl(cidr.u.in.mask);
         }
 
-        cidr.u.in.addr = ntohl(cidr.u.in.addr);
-        cidr.u.in.mask = ntohl(cidr.u.in.mask);
+        if (del) {
+            switch (cidr.family) {
 
-        if (del) {
-            if (ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr,
-                                       cidr.u.in.mask)
-                != NGX_OK)
-            {
+#if (NGX_HAVE_INET6)
+            case AF_INET6:
+                rc = ngx_radix128tree_delete(ctx->tree6,
+                                             cidr.u.in6.addr.s6_addr,
+                                             cidr.u.in6.mask.s6_addr);
+                break;
+#endif
+
+            default: /* AF_INET */
+                rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr,
+                                            cidr.u.in.mask);
+                break;
+            }
+
+            if (rc != NGX_OK) {
                 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                                    "no network \"%V\" to delete", net);
             }
@@ -982,32 +1093,78 @@
         return NGX_CONF_ERROR;
     }
 
-    for (i = 2; i; i--) {
-        rc = ngx_radix32tree_insert(ctx->tree, cidr.u.in.addr, cidr.u.in.mask,
-                                    (uintptr_t) val);
-        if (rc == NGX_OK) {
-            return NGX_CONF_OK;
-        }
+    switch (cidr.family) {
 
-        if (rc == NGX_ERROR) {
-            return NGX_CONF_ERROR;
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+        for (i = 2; i; i--) {
+            rc = ngx_radix128tree_insert(ctx->tree6, cidr.u.in6.addr.s6_addr,
+                                         cidr.u.in6.mask.s6_addr,
+                                         (uintptr_t) val);
+
+            if (rc == NGX_OK) {
+                return NGX_CONF_OK;
+            }
+
+            if (rc == NGX_ERROR) {
+                return NGX_CONF_ERROR;
+            }
+
+            /* rc == NGX_BUSY */
+
+            old = (ngx_http_variable_value_t *)
+                       ngx_radix128tree_find(ctx->tree6,
+                                             cidr.u.in6.addr.s6_addr);
+
+            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                  "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
+                  net, val, old);
+
+            rc = ngx_radix128tree_delete(ctx->tree6,
+                                         cidr.u.in6.addr.s6_addr,
+                                         cidr.u.in6.mask.s6_addr);
+
+            if (rc == NGX_ERROR) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
+                return NGX_CONF_ERROR;
+            }
         }
 
-        /* rc == NGX_BUSY */
+        break;
+#endif
 
-        old = (ngx_http_variable_value_t *)
-              ngx_radix32tree_find(ctx->tree, cidr.u.in.addr);
+    default: /* AF_INET */
+        for (i = 2; i; i--) {
+            rc = ngx_radix32tree_insert(ctx->tree, cidr.u.in.addr,
+                                        cidr.u.in.mask, (uintptr_t) val);
 
-        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
-                "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
-                net, val, old);
+            if (rc == NGX_OK) {
+                return NGX_CONF_OK;
+            }
 
-        rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr, cidr.u.in.mask);
+            if (rc == NGX_ERROR) {
+                return NGX_CONF_ERROR;
+            }
 
-        if (rc == NGX_ERROR) {
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
-            return NGX_CONF_ERROR;
+            /* rc == NGX_BUSY */
+
+            old = (ngx_http_variable_value_t *)
+                       ngx_radix32tree_find(ctx->tree, cidr.u.in.addr);
+
+            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                  "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
+                  net, val, old);
+
+            rc = ngx_radix32tree_delete(ctx->tree,
+                                        cidr.u.in.addr, cidr.u.in.mask);
+
+            if (rc == NGX_ERROR) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
+                return NGX_CONF_ERROR;
+            }
         }
+
+        break;
     }
 
     return NGX_CONF_ERROR;



More information about the nginx-devel mailing list