comparison src/http/modules/ngx_http_geo_module.c @ 4992:3be3de31d7dd

Geo: IPv6 support. The "ranges" mode is still limited to IPv4 only.
author Ruslan Ermilov <ru@nginx.com>
date Tue, 25 Dec 2012 08:21:56 +0000
parents eababa1dc446
children a8cc59ead621
comparison
equal deleted inserted replaced
4991:a384c60d55f3 4992:3be3de31d7dd
16 u_short end; 16 u_short end;
17 } ngx_http_geo_range_t; 17 } ngx_http_geo_range_t;
18 18
19 19
20 typedef struct { 20 typedef struct {
21 ngx_radix_tree_t *tree;
22 #if (NGX_HAVE_INET6)
23 ngx_radix_tree_t *tree6;
24 #endif
25 } ngx_http_geo_trees_t;
26
27
28 typedef struct {
21 ngx_http_geo_range_t **low; 29 ngx_http_geo_range_t **low;
22 ngx_http_variable_value_t *default_value; 30 ngx_http_variable_value_t *default_value;
23 } ngx_http_geo_high_ranges_t; 31 } ngx_http_geo_high_ranges_t;
24 32
25 33
33 typedef struct { 41 typedef struct {
34 ngx_http_variable_value_t *value; 42 ngx_http_variable_value_t *value;
35 ngx_str_t *net; 43 ngx_str_t *net;
36 ngx_http_geo_high_ranges_t high; 44 ngx_http_geo_high_ranges_t high;
37 ngx_radix_tree_t *tree; 45 ngx_radix_tree_t *tree;
46 #if (NGX_HAVE_INET6)
47 ngx_radix_tree_t *tree6;
48 #endif
38 ngx_rbtree_t rbtree; 49 ngx_rbtree_t rbtree;
39 ngx_rbtree_node_t sentinel; 50 ngx_rbtree_node_t sentinel;
40 ngx_array_t *proxies; 51 ngx_array_t *proxies;
41 ngx_pool_t *pool; 52 ngx_pool_t *pool;
42 ngx_pool_t *temp_pool; 53 ngx_pool_t *temp_pool;
55 } ngx_http_geo_conf_ctx_t; 66 } ngx_http_geo_conf_ctx_t;
56 67
57 68
58 typedef struct { 69 typedef struct {
59 union { 70 union {
60 ngx_radix_tree_t *tree; 71 ngx_http_geo_trees_t trees;
61 ngx_http_geo_high_ranges_t high; 72 ngx_http_geo_high_ranges_t high;
62 } u; 73 } u;
63 74
64 ngx_array_t *proxies; 75 ngx_array_t *proxies;
65 unsigned proxy_recursive:1; 76 unsigned proxy_recursive:1;
66 77
67 ngx_int_t index; 78 ngx_int_t index;
68 } ngx_http_geo_ctx_t; 79 } ngx_http_geo_ctx_t;
69 80
70 81
71 static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r, 82 static ngx_int_t ngx_http_geo_addr(ngx_http_request_t *r,
72 ngx_http_geo_ctx_t *ctx); 83 ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);
73 static ngx_int_t ngx_http_geo_real_addr(ngx_http_request_t *r, 84 static ngx_int_t ngx_http_geo_real_addr(ngx_http_request_t *r,
74 ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr); 85 ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);
75 static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 86 static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
76 static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); 87 static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
77 static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, 88 static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
153 static ngx_http_geo_header_t ngx_http_geo_header = { 164 static ngx_http_geo_header_t ngx_http_geo_header = {
154 { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0 165 { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0
155 }; 166 };
156 167
157 168
158 /* AF_INET only */ 169 /* geo range is AF_INET only */
159 170
160 static ngx_int_t 171 static ngx_int_t
161 ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, 172 ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
162 uintptr_t data) 173 uintptr_t data)
163 { 174 {
164 ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data; 175 ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
165 176
177 in_addr_t inaddr;
178 ngx_addr_t addr;
179 struct sockaddr_in *sin;
166 ngx_http_variable_value_t *vv; 180 ngx_http_variable_value_t *vv;
167 181 #if (NGX_HAVE_INET6)
168 vv = (ngx_http_variable_value_t *) 182 u_char *p;
169 ngx_radix32tree_find(ctx->u.tree, ngx_http_geo_addr(r, ctx)); 183 struct in6_addr *inaddr6;
184 #endif
185
186 if (ngx_http_geo_addr(r, ctx, &addr) != NGX_OK) {
187 vv = (ngx_http_variable_value_t *)
188 ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE);
189 goto done;
190 }
191
192 switch (addr.sockaddr->sa_family) {
193
194 #if (NGX_HAVE_INET6)
195 case AF_INET6:
196 inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
197 p = inaddr6->s6_addr;
198
199 if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
200 inaddr = p[12] << 24;
201 inaddr += p[13] << 16;
202 inaddr += p[14] << 8;
203 inaddr += p[15];
204
205 vv = (ngx_http_variable_value_t *)
206 ngx_radix32tree_find(ctx->u.trees.tree, inaddr);
207
208 } else {
209 vv = (ngx_http_variable_value_t *)
210 ngx_radix128tree_find(ctx->u.trees.tree6, p);
211 }
212
213 break;
214 #endif
215
216 default: /* AF_INET */
217 sin = (struct sockaddr_in *) addr.sockaddr;
218 inaddr = ntohl(sin->sin_addr.s_addr);
219
220 vv = (ngx_http_variable_value_t *)
221 ngx_radix32tree_find(ctx->u.trees.tree, inaddr);
222
223 break;
224 }
225
226 done:
170 227
171 *v = *vv; 228 *v = *vv;
172 229
173 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 230 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
174 "http geo: %v", v); 231 "http geo: %v", v);
181 ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, 238 ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
182 uintptr_t data) 239 uintptr_t data)
183 { 240 {
184 ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data; 241 ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
185 242
186 in_addr_t addr; 243 in_addr_t inaddr;
244 ngx_addr_t addr;
187 ngx_uint_t n; 245 ngx_uint_t n;
246 struct sockaddr_in *sin;
188 ngx_http_geo_range_t *range; 247 ngx_http_geo_range_t *range;
248 #if (NGX_HAVE_INET6)
249 u_char *p;
250 struct in6_addr *inaddr6;
251 #endif
189 252
190 *v = *ctx->u.high.default_value; 253 *v = *ctx->u.high.default_value;
191 254
255 if (ngx_http_geo_addr(r, ctx, &addr) == NGX_OK) {
256
257 switch (addr.sockaddr->sa_family) {
258
259 #if (NGX_HAVE_INET6)
260 case AF_INET6:
261 inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
262
263 if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
264 p = inaddr6->s6_addr;
265
266 inaddr = p[12] << 24;
267 inaddr += p[13] << 16;
268 inaddr += p[14] << 8;
269 inaddr += p[15];
270
271 } else {
272 inaddr = INADDR_NONE;
273 }
274
275 break;
276 #endif
277
278 default: /* AF_INET */
279 sin = (struct sockaddr_in *) addr.sockaddr;
280 inaddr = ntohl(sin->sin_addr.s_addr);
281 break;
282 }
283
284 } else {
285 inaddr = INADDR_NONE;
286 }
287
192 if (ctx->u.high.low) { 288 if (ctx->u.high.low) {
193 addr = ngx_http_geo_addr(r, ctx); 289 range = ctx->u.high.low[inaddr >> 16];
194
195 range = ctx->u.high.low[addr >> 16];
196 290
197 if (range) { 291 if (range) {
198 n = addr & 0xffff; 292 n = inaddr & 0xffff;
199 do { 293 do {
200 if (n >= (ngx_uint_t) range->start 294 if (n >= (ngx_uint_t) range->start
201 && n <= (ngx_uint_t) range->end) 295 && n <= (ngx_uint_t) range->end)
202 { 296 {
203 *v = *range->value; 297 *v = *range->value;
212 306
213 return NGX_OK; 307 return NGX_OK;
214 } 308 }
215 309
216 310
217 static in_addr_t 311 static ngx_int_t
218 ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx) 312 ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,
313 ngx_addr_t *addr)
219 { 314 {
220 ngx_addr_t addr; 315 ngx_table_elt_t *xfwd;
221 ngx_table_elt_t *xfwd; 316
222 struct sockaddr_in *sin; 317 if (ngx_http_geo_real_addr(r, ctx, addr) != NGX_OK) {
223 318 return NGX_ERROR;
224 if (ngx_http_geo_real_addr(r, ctx, &addr) != NGX_OK) {
225 return INADDR_NONE;
226 } 319 }
227 320
228 xfwd = r->headers_in.x_forwarded_for; 321 xfwd = r->headers_in.x_forwarded_for;
229 322
230 if (xfwd != NULL && ctx->proxies != NULL) { 323 if (xfwd != NULL && ctx->proxies != NULL) {
231 (void) ngx_http_get_forwarded_addr(r, &addr, xfwd->value.data, 324 (void) ngx_http_get_forwarded_addr(r, addr, xfwd->value.data,
232 xfwd->value.len, ctx->proxies, 325 xfwd->value.len, ctx->proxies,
233 ctx->proxy_recursive); 326 ctx->proxy_recursive);
234 } 327 }
235 328
236 #if (NGX_HAVE_INET6) 329 return NGX_OK;
237
238 if (addr.sockaddr->sa_family == AF_INET6) {
239 u_char *p;
240 in_addr_t inaddr;
241 struct in6_addr *inaddr6;
242
243 inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
244
245 if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
246 p = inaddr6->s6_addr;
247
248 inaddr = p[12] << 24;
249 inaddr += p[13] << 16;
250 inaddr += p[14] << 8;
251 inaddr += p[15];
252
253 return inaddr;
254 }
255 }
256
257 #endif
258
259 if (addr.sockaddr->sa_family != AF_INET) {
260 return INADDR_NONE;
261 }
262
263 sin = (struct sockaddr_in *) addr.sockaddr;
264 return ntohl(sin->sin_addr.s_addr);
265 } 330 }
266 331
267 332
268 static ngx_int_t 333 static ngx_int_t
269 ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx, 334 ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,
313 ngx_pool_t *pool; 378 ngx_pool_t *pool;
314 ngx_array_t *a; 379 ngx_array_t *a;
315 ngx_http_variable_t *var; 380 ngx_http_variable_t *var;
316 ngx_http_geo_ctx_t *geo; 381 ngx_http_geo_ctx_t *geo;
317 ngx_http_geo_conf_ctx_t ctx; 382 ngx_http_geo_conf_ctx_t ctx;
383 #if (NGX_HAVE_INET6)
384 static struct in6_addr zero;
385 #endif
318 386
319 value = cf->args->elts; 387 value = cf->args->elts;
320 388
321 geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t)); 389 geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t));
322 if (geo == NULL) { 390 if (geo == NULL) {
443 if (ctx.tree == NULL) { 511 if (ctx.tree == NULL) {
444 return NGX_CONF_ERROR; 512 return NGX_CONF_ERROR;
445 } 513 }
446 } 514 }
447 515
448 geo->u.tree = ctx.tree; 516 geo->u.trees.tree = ctx.tree;
517
518 #if (NGX_HAVE_INET6)
519 if (ctx.tree6 == NULL) {
520 ctx.tree6 = ngx_radix_tree_create(cf->pool, -1);
521 if (ctx.tree6 == NULL) {
522 return NGX_CONF_ERROR;
523 }
524 }
525
526 geo->u.trees.tree6 = ctx.tree6;
527 #endif
449 528
450 var->get_handler = ngx_http_geo_cidr_variable; 529 var->get_handler = ngx_http_geo_cidr_variable;
451 var->data = (uintptr_t) geo; 530 var->data = (uintptr_t) geo;
452 531
453 ngx_destroy_pool(ctx.temp_pool); 532 ngx_destroy_pool(ctx.temp_pool);
459 { 538 {
460 return NGX_CONF_ERROR; 539 return NGX_CONF_ERROR;
461 } 540 }
462 541
463 /* NGX_BUSY is okay (default was set explicitly) */ 542 /* NGX_BUSY is okay (default was set explicitly) */
543
544 #if (NGX_HAVE_INET6)
545 if (ngx_radix128tree_insert(ctx.tree6, zero.s6_addr, zero.s6_addr,
546 (uintptr_t) &ngx_http_variable_null_value)
547 == NGX_ERROR)
548 {
549 return NGX_CONF_ERROR;
550 }
551 #endif
464 } 552 }
465 553
466 return rv; 554 return rv;
467 } 555 }
468 556
481 569
482 if (cf->args->nelts == 1) { 570 if (cf->args->nelts == 1) {
483 571
484 if (ngx_strcmp(value[0].data, "ranges") == 0) { 572 if (ngx_strcmp(value[0].data, "ranges") == 0) {
485 573
486 if (ctx->tree) { 574 if (ctx->tree
575 #if (NGX_HAVE_INET6)
576 || ctx->tree6
577 #endif
578 )
579 {
487 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 580 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
488 "the \"ranges\" directive must be " 581 "the \"ranges\" directive must be "
489 "the first directive inside \"geo\" block"); 582 "the first directive inside \"geo\" block");
490 goto failed; 583 goto failed;
491 } 584 }
932 if (ctx->tree == NULL) { 1025 if (ctx->tree == NULL) {
933 return NGX_CONF_ERROR; 1026 return NGX_CONF_ERROR;
934 } 1027 }
935 } 1028 }
936 1029
1030 #if (NGX_HAVE_INET6)
1031 if (ctx->tree6 == NULL) {
1032 ctx->tree6 = ngx_radix_tree_create(ctx->pool, -1);
1033 if (ctx->tree6 == NULL) {
1034 return NGX_CONF_ERROR;
1035 }
1036 }
1037 #endif
1038
937 if (ngx_strcmp(value[0].data, "default") == 0) { 1039 if (ngx_strcmp(value[0].data, "default") == 0) {
938 /* cidr.family = AF_INET; */ 1040 /* cidr.family = AF_INET; */
939 cidr.u.in.addr = 0; 1041 cidr.u.in.addr = 0;
940 cidr.u.in.mask = 0; 1042 cidr.u.in.mask = 0;
941 net = &value[0]; 1043 net = &value[0];
952 1054
953 if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) { 1055 if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) {
954 return NGX_CONF_ERROR; 1056 return NGX_CONF_ERROR;
955 } 1057 }
956 1058
957 if (cidr.family != AF_INET) { 1059 if (cidr.family == AF_INET) {
958 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 1060 cidr.u.in.addr = ntohl(cidr.u.in.addr);
959 "\"geo\" supports IPv4 only"); 1061 cidr.u.in.mask = ntohl(cidr.u.in.mask);
960 return NGX_CONF_ERROR; 1062 }
961 }
962
963 cidr.u.in.addr = ntohl(cidr.u.in.addr);
964 cidr.u.in.mask = ntohl(cidr.u.in.mask);
965 1063
966 if (del) { 1064 if (del) {
967 if (ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr, 1065 switch (cidr.family) {
968 cidr.u.in.mask) 1066
969 != NGX_OK) 1067 #if (NGX_HAVE_INET6)
970 { 1068 case AF_INET6:
1069 rc = ngx_radix128tree_delete(ctx->tree6,
1070 cidr.u.in6.addr.s6_addr,
1071 cidr.u.in6.mask.s6_addr);
1072 break;
1073 #endif
1074
1075 default: /* AF_INET */
1076 rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr,
1077 cidr.u.in.mask);
1078 break;
1079 }
1080
1081 if (rc != NGX_OK) {
971 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, 1082 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
972 "no network \"%V\" to delete", net); 1083 "no network \"%V\" to delete", net);
973 } 1084 }
974 1085
975 return NGX_CONF_OK; 1086 return NGX_CONF_OK;
980 1091
981 if (val == NULL) { 1092 if (val == NULL) {
982 return NGX_CONF_ERROR; 1093 return NGX_CONF_ERROR;
983 } 1094 }
984 1095
985 for (i = 2; i; i--) { 1096 switch (cidr.family) {
986 rc = ngx_radix32tree_insert(ctx->tree, cidr.u.in.addr, cidr.u.in.mask, 1097
987 (uintptr_t) val); 1098 #if (NGX_HAVE_INET6)
988 if (rc == NGX_OK) { 1099 case AF_INET6:
989 return NGX_CONF_OK; 1100 for (i = 2; i; i--) {
990 } 1101 rc = ngx_radix128tree_insert(ctx->tree6, cidr.u.in6.addr.s6_addr,
991 1102 cidr.u.in6.mask.s6_addr,
992 if (rc == NGX_ERROR) { 1103 (uintptr_t) val);
993 return NGX_CONF_ERROR; 1104
994 } 1105 if (rc == NGX_OK) {
995 1106 return NGX_CONF_OK;
996 /* rc == NGX_BUSY */ 1107 }
997 1108
998 old = (ngx_http_variable_value_t *) 1109 if (rc == NGX_ERROR) {
999 ngx_radix32tree_find(ctx->tree, cidr.u.in.addr); 1110 return NGX_CONF_ERROR;
1000 1111 }
1001 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, 1112
1002 "duplicate network \"%V\", value: \"%v\", old value: \"%v\"", 1113 /* rc == NGX_BUSY */
1003 net, val, old); 1114
1004 1115 old = (ngx_http_variable_value_t *)
1005 rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr, cidr.u.in.mask); 1116 ngx_radix128tree_find(ctx->tree6,
1006 1117 cidr.u.in6.addr.s6_addr);
1007 if (rc == NGX_ERROR) { 1118
1008 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree"); 1119 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1009 return NGX_CONF_ERROR; 1120 "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
1010 } 1121 net, val, old);
1122
1123 rc = ngx_radix128tree_delete(ctx->tree6,
1124 cidr.u.in6.addr.s6_addr,
1125 cidr.u.in6.mask.s6_addr);
1126
1127 if (rc == NGX_ERROR) {
1128 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
1129 return NGX_CONF_ERROR;
1130 }
1131 }
1132
1133 break;
1134 #endif
1135
1136 default: /* AF_INET */
1137 for (i = 2; i; i--) {
1138 rc = ngx_radix32tree_insert(ctx->tree, cidr.u.in.addr,
1139 cidr.u.in.mask, (uintptr_t) val);
1140
1141 if (rc == NGX_OK) {
1142 return NGX_CONF_OK;
1143 }
1144
1145 if (rc == NGX_ERROR) {
1146 return NGX_CONF_ERROR;
1147 }
1148
1149 /* rc == NGX_BUSY */
1150
1151 old = (ngx_http_variable_value_t *)
1152 ngx_radix32tree_find(ctx->tree, cidr.u.in.addr);
1153
1154 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
1155 "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
1156 net, val, old);
1157
1158 rc = ngx_radix32tree_delete(ctx->tree,
1159 cidr.u.in.addr, cidr.u.in.mask);
1160
1161 if (rc == NGX_ERROR) {
1162 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
1163 return NGX_CONF_ERROR;
1164 }
1165 }
1166
1167 break;
1011 } 1168 }
1012 1169
1013 return NGX_CONF_ERROR; 1170 return NGX_CONF_ERROR;
1014 } 1171 }
1015 1172