Klaus Demo nginx / 1c6c633
*) descrease geo configuration memory usage *) geo delete *) geo ranges Igor Sysoev 13 years ago
1 changed file(s) with 542 addition(s) and 85 deletion(s). Raw diff Collapse all Expand all
99
1010
1111 typedef struct {
12 ngx_radix_tree_t *tree;
13 ngx_rbtree_t rbtree;
14 ngx_rbtree_node_t sentinel;
15 ngx_pool_t *pool;
12 u_short start;
13 u_short end;
14 ngx_http_variable_value_t *value;
15 } ngx_http_geo_range_t;
16
17
18 typedef struct {
19 ngx_http_geo_range_t *ranges;
20 ngx_uint_t n;
21 } ngx_http_geo_low_ranges_t;
22
23
24 typedef struct {
25 ngx_http_geo_low_ranges_t low[0x10000];
26 ngx_http_variable_value_t *default_value;
27 } ngx_http_geo_high_ranges_t;
28
29
30 typedef struct {
31 ngx_http_variable_value_t *value;
32 ngx_str_t *net;
33 ngx_http_geo_high_ranges_t *high;
34 ngx_radix_tree_t *tree;
35 ngx_rbtree_t rbtree;
36 ngx_rbtree_node_t sentinel;
37 ngx_pool_t *pool;
38 ngx_pool_t *temp_pool;
1639 } ngx_http_geo_conf_ctx_t;
1740
1841
1942 static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
2043 static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
44 static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
45 ngx_str_t *value);
46 static char *ngx_http_geo_add_range(ngx_conf_t *cf,
47 ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
48 static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf,
49 ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
50 static char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
51 ngx_str_t *value);
52 static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf,
53 ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value);
2154
2255
2356 static ngx_command_t ngx_http_geo_commands[] = {
67100 /* AF_INET only */
68101
69102 static ngx_int_t
70 ngx_http_geo_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
103 ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
71104 uintptr_t data)
72105 {
73106 ngx_radix_tree_t *tree = (ngx_radix_tree_t *) data;
75108 struct sockaddr_in *sin;
76109 ngx_http_variable_value_t *vv;
77110
78 sin = (struct sockaddr_in *) r->connection->sockaddr;
79
80111 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
81112 "http geo started");
82113
114 sin = (struct sockaddr_in *) r->connection->sockaddr;
115
83116 vv = (ngx_http_variable_value_t *)
84117 ngx_radix32tree_find(tree, ntohl(sin->sin_addr.s_addr));
85118
92125 }
93126
94127
128 static ngx_int_t
129 ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
130 uintptr_t data)
131 {
132 ngx_http_geo_high_ranges_t *high = (ngx_http_geo_high_ranges_t *) data;
133
134 in_addr_t addr;
135 ngx_uint_t i, n;
136 struct sockaddr_in *sin;
137 ngx_http_geo_range_t *range;
138
139 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
140 "http geo started");
141
142 sin = (struct sockaddr_in *) r->connection->sockaddr;
143
144 *v = *high->default_value;
145
146 addr = ntohl(sin->sin_addr.s_addr);
147
148 range = high->low[addr >> 16].ranges;
149
150 n = addr & 0xffff;
151
152 for (i = 0; i < high->low[addr >> 16].n; i++) {
153 if (n >= (ngx_uint_t) range[i].start
154 && n <= (ngx_uint_t) range[i].end)
155 {
156 *v = *range[i].value;
157 }
158 }
159
160 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
161 "http geo: %V %v", &r->connection->addr_text, v);
162
163 return NGX_OK;
164 }
165
166
95167 static char *
96168 ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
97169 {
98170 char *rv;
171 size_t len;
99172 ngx_str_t *value, name;
173 ngx_uint_t i;
100174 ngx_conf_t save;
101175 ngx_pool_t *pool;
102 ngx_radix_tree_t *tree;
176 ngx_array_t *a;
103177 ngx_http_variable_t *var;
104178 ngx_http_geo_conf_ctx_t ctx;
105179
121195 return NGX_CONF_ERROR;
122196 }
123197
124 tree = ngx_radix_tree_create(cf->pool, -1);
125
126 if (tree == NULL) {
127 return NGX_CONF_ERROR;
128 }
129
130 var->get_handler = ngx_http_geo_variable;
131 var->data = (uintptr_t) tree;
132
133198 pool = ngx_create_pool(16384, cf->log);
134199 if (pool == NULL) {
135200 return NGX_CONF_ERROR;
136201 }
137202
203 ctx.temp_pool = ngx_create_pool(16384, cf->log);
204 if (ctx.temp_pool == NULL) {
205 return NGX_CONF_ERROR;
206 }
207
138208 ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel,
139209 ngx_http_variable_value_rbtree_insert);
140210
141 ctx.tree = tree;
211 ctx.high = NULL;
212 ctx.tree = NULL;
142213 ctx.pool = cf->pool;
143214
144215 save = *cf;
151222
152223 *cf = save;
153224
154 ngx_destroy_pool(pool);
155
156 if (ngx_radix32tree_find(tree, 0) != NGX_RADIX_NO_VALUE) {
157 return rv;
158 }
159
160 if (ngx_radix32tree_insert(tree, 0, 0,
161 (uintptr_t) &ngx_http_variable_null_value)
162 == NGX_ERROR)
163 {
164 return NGX_CONF_ERROR;
225 if (ctx.high) {
226
227 for (i = 0; i < 0x10000; i++) {
228 a = (ngx_array_t *) ctx.high->low[i].ranges;
229
230 if (a == NULL || a->nelts == 0) {
231 continue;
232 }
233
234 ctx.high->low[i].n = a->nelts;
235
236 len = a->nelts * sizeof(ngx_http_geo_range_t);
237
238 ctx.high->low[i].ranges = ngx_palloc(cf->pool, len);
239 if (ctx.high->low[i].ranges == NULL ){
240 return NGX_CONF_ERROR;
241 }
242
243 ngx_memcpy(ctx.high->low[i].ranges, a->elts, len);
244 }
245
246 var->get_handler = ngx_http_geo_range_variable;
247 var->data = (uintptr_t) ctx.high;
248
249 ngx_destroy_pool(ctx.temp_pool);
250 ngx_destroy_pool(pool);
251
252 if (ctx.high->default_value == NULL) {
253 ctx.high->default_value = &ngx_http_variable_null_value;
254 }
255
256 } else {
257 var->get_handler = ngx_http_geo_cidr_variable;
258 var->data = (uintptr_t) ctx.tree;
259
260 ngx_destroy_pool(ctx.temp_pool);
261 ngx_destroy_pool(pool);
262
263 if (ngx_radix32tree_find(ctx.tree, 0) != NGX_RADIX_NO_VALUE) {
264 return rv;
265 }
266
267 if (ngx_radix32tree_insert(ctx.tree, 0, 0,
268 (uintptr_t) &ngx_http_variable_null_value)
269 == NGX_ERROR)
270 {
271 return NGX_CONF_ERROR;
272 }
165273 }
166274
167275 return rv;
168276 }
169277
170
171 /* AF_INET only */
172278
173279 static char *
174280 ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
175281 {
176 uint32_t hash;
177 ngx_int_t rc;
178 ngx_str_t *value, file;
179 ngx_uint_t i;
180 ngx_inet_cidr_t cidrin;
181 ngx_http_geo_conf_ctx_t *ctx;
182 ngx_http_variable_value_t *val, *old;
183 ngx_http_variable_value_node_t *vvn;
282 char *rv;
283 ngx_str_t *value, file;
284 ngx_http_geo_conf_ctx_t *ctx;
184285
185286 ctx = cf->ctx;
287
288 value = cf->args->elts;
289
290 if (cf->args->nelts == 1) {
291
292 if (ngx_strcmp(value[0].data, "ranges") == 0) {
293
294 if (ctx->tree) {
295 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
296 "the \"ranges\" directive must be "
297 "the first directive inside \"geo\" block");
298 goto failed;
299 }
300
301 ctx->high = ngx_pcalloc(ctx->pool,
302 sizeof(ngx_http_geo_high_ranges_t));
303 if (ctx->high == NULL) {
304 goto failed;
305 }
306
307 rv = NGX_CONF_OK;
308
309 goto done;
310 }
311 }
186312
187313 if (cf->args->nelts != 2) {
188314 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
189315 "invalid number of the geo parameters");
316 goto failed;
317 }
318
319 if (ngx_strcmp(value[0].data, "include") == 0) {
320
321 file.len = value[1].len++;
322
323 file.data = ngx_pstrdup(ctx->temp_pool, &value[1]);
324 if (file.data == NULL) {
325 goto failed;
326 }
327
328 if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK){
329 goto failed;
330 }
331
332 ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
333
334 rv = ngx_conf_parse(cf, &file);
335
336 goto done;
337 }
338
339 if (ctx->high) {
340 rv = ngx_http_geo_range(cf, ctx, value);
341
342 } else {
343 rv = ngx_http_geo_cidr(cf, ctx, value);
344 }
345
346 done:
347
348 ngx_reset_pool(cf->pool);
349
350 return rv;
351
352 failed:
353
354 ngx_reset_pool(cf->pool);
355
356 return NGX_CONF_ERROR;
357 }
358
359
360 static char *
361 ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
362 ngx_str_t *value)
363 {
364 u_char *p, *last;
365 in_addr_t start, end;
366 ngx_str_t *net;
367 ngx_uint_t del;
368 ngx_http_variable_value_t *old;
369
370 if (ngx_strcmp(value[0].data, "default") == 0) {
371
372 old = ctx->high->default_value;
373
374 ctx->high->default_value = ngx_http_geo_value(cf, ctx, &value[1]);
375 if (ctx->high->default_value == NULL) {
376 return NGX_CONF_ERROR;
377 }
378
379 if (old) {
380 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
381 "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
382 &value[0], ctx->high->default_value, old);
383 }
384
385 return NGX_CONF_OK;
386 }
387
388 if (ngx_strcmp(value[0].data, "delete") == 0) {
389 net = &value[1];
390 del = 1;
391
392 } else {
393 net = &value[0];
394 del = 0;
395 }
396
397 last = net->data + net->len;
398
399 p = ngx_strlchr(net->data, last, '-');
400
401 if (p == NULL) {
402 goto invalid;
403 }
404
405 start = ngx_inet_addr(net->data, p - net->data);
406
407 if (start == INADDR_NONE) {
408 goto invalid;
409 }
410
411 start = ntohl(start);
412
413 p++;
414
415 end = ngx_inet_addr(p, last - p);
416
417 if (end == INADDR_NONE) {
418 goto invalid;
419 }
420
421 end = ntohl(end);
422
423 if (start > end) {
424 goto invalid;
425 }
426
427 if (del) {
428 if (ngx_http_geo_delete_range(cf, ctx, start, end)) {
429 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
430 "no address range \"%V\" to delete", net);
431 }
432
433 return NGX_CONF_OK;
434 }
435
436 ctx->value = ngx_http_geo_value(cf, ctx, &value[1]);
437
438 if (ctx->value == NULL) {
190439 return NGX_CONF_ERROR;
191440 }
192441
193 value = cf->args->elts;
194
195 if (ngx_strcmp(value[0].data, "include") == 0) {
196 file = value[1];
197
198 if (ngx_conf_full_name(cf->cycle, &file, 1) == NGX_ERROR){
442 ctx->net = net;
443
444 return ngx_http_geo_add_range(cf, ctx, start, end);
445
446 invalid:
447
448 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net);
449
450 return NGX_CONF_ERROR;
451 }
452
453
454 /* the add procedure is optimized to add a growing up sequence */
455
456 static char *
457 ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
458 in_addr_t start, in_addr_t end)
459 {
460 in_addr_t n;
461 ngx_uint_t h, i, s, e;
462 ngx_array_t *a;
463 ngx_http_geo_range_t *range;
464
465 for (n = start; n < end; n += 0x10000) {
466
467 h = n >> 16;
468 s = n & 0xffff;
469
470 if ((n | 0xffff) > end) {
471 e = end & 0xffff;
472
473 } else {
474 e = 0xffff;
475 }
476
477 a = (ngx_array_t *) ctx->high->low[h].ranges;
478
479 if (a == NULL) {
480 a = ngx_array_create(ctx->temp_pool, 64,
481 sizeof(ngx_http_geo_range_t));
482 if (a == NULL) {
483 return NGX_CONF_ERROR;
484 }
485
486 ctx->high->low[h].ranges = (ngx_http_geo_range_t *) a;
487 }
488
489 i = a->nelts;
490 range = a->elts;
491
492 while (i) {
493
494 i--;
495
496 if (e < (ngx_uint_t) range[i].start) {
497 continue;
498 }
499
500 if (s > (ngx_uint_t) range[i].end) {
501
502 /* add after the range */
503
504 range = ngx_array_push(a);
505 if (range == NULL) {
506 return NGX_CONF_ERROR;
507 }
508
509 range = a->elts;
510
511 ngx_memcpy(&range[i + 2], &range[i + 1],
512 (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
513
514 range = &range[i + 1];
515
516 goto next;
517 }
518
519 if (s == (ngx_uint_t) range[i].start
520 && e == (ngx_uint_t) range[i].end)
521 {
522 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
523 "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
524 ctx->net, ctx->value, range[i].value);
525 continue;
526 }
527
528 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
529 "overlapped range \"%V\"", ctx->net);
530
199531 return NGX_CONF_ERROR;
200532 }
201533
202 ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
203
204 return ngx_conf_parse(cf, &file);
534 /* add the first range */
535
536 range = ngx_array_push(a);
537 if (range == NULL) {
538 return NGX_CONF_ERROR;
539 }
540
541 next:
542
543 range->start = (u_short) s;
544 range->end = (u_short) e;
545 range->value = ctx->value;
546 }
547
548 return NGX_CONF_OK;
549 }
550
551
552 static ngx_uint_t
553 ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
554 in_addr_t start, in_addr_t end)
555 {
556 in_addr_t n;
557 ngx_uint_t h, i, s, e, warn;
558 ngx_array_t *a;
559 ngx_http_geo_range_t *range;
560
561 warn = 0;
562
563 for (n = start; n < end; n += 0x10000) {
564
565 h = n >> 16;
566 s = n & 0xffff;
567
568 if ((n | 0xffff) > end) {
569 e = end & 0xffff;
570
571 } else {
572 e = 0xffff;
573 }
574
575 a = (ngx_array_t *) ctx->high->low[h].ranges;
576
577 if (a == NULL) {
578 warn = 1;
579 continue;
580 }
581
582 range = a->elts;
583 for (i = 0; i < a->nelts; i++) {
584
585 if (s == (ngx_uint_t) range[i].start
586 && e == (ngx_uint_t) range[i].end)
587 {
588 ngx_memcpy(&range[i], &range[i + 1],
589 (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
590 break;
591 }
592
593 if (s != (ngx_uint_t) range[i].start
594 && e != (ngx_uint_t) range[i].end)
595 {
596 continue;
597 }
598
599 warn = 1;
600 }
601 }
602
603 return warn;
604 }
605
606
607 static char *
608 ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
609 ngx_str_t *value)
610 {
611 ngx_int_t rc, del;
612 ngx_str_t *net;
613 ngx_uint_t i;
614 ngx_inet_cidr_t cidrin;
615 ngx_http_variable_value_t *val, *old;
616
617 if (ctx->tree == NULL) {
618 ctx->tree = ngx_radix_tree_create(ctx->pool, -1);
619 if (ctx->tree == NULL) {
620 return NGX_CONF_ERROR;
621 }
205622 }
206623
207624 if (ngx_strcmp(value[0].data, "default") == 0) {
208625 cidrin.addr = 0;
209626 cidrin.mask = 0;
627 net = &value[0];
210628
211629 } else {
212 rc = ngx_ptocidr(&value[0], &cidrin);
630 if (ngx_strcmp(value[0].data, "delete") == 0) {
631 net = &value[1];
632 del = 1;
633
634 } else {
635 net = &value[0];
636 del = 0;
637 }
638
639 rc = ngx_ptocidr(net, &cidrin);
213640
214641 if (rc == NGX_ERROR) {
215642 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
216 "invalid parameter \"%V\"", &value[0]);
643 "invalid network \"%V\"", net);
217644 return NGX_CONF_ERROR;
218645 }
219646
220647 if (rc == NGX_DONE) {
221648 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
222 "low address bits of %V are meaningless",
223 &value[0]);
649 "low address bits of %V are meaningless", net);
224650 }
225651
226652 cidrin.addr = ntohl(cidrin.addr);
227653 cidrin.mask = ntohl(cidrin.mask);
228 }
229
230 hash = ngx_crc32_long(value[1].data, value[1].len);
231
232 val = ngx_http_variable_value_lookup(&ctx->rbtree, &value[1], hash);
654
655 if (del) {
656 if (ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask)
657 != NGX_OK)
658 {
659 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
660 "no network \"%V\" to delete", net);
661 return NGX_CONF_OK;
662 }
663 }
664 }
665
666 val = ngx_http_geo_value(cf, ctx, &value[1]);
233667
234668 if (val == NULL) {
235 val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
236 if (val == NULL) {
237 return NGX_CONF_ERROR;
238 }
239
240 val->len = value[1].len;
241 val->data = ngx_pstrdup(ctx->pool, &value[1]);
242 if (val->data == NULL) {
243 return NGX_CONF_ERROR;
244 }
245
246 val->valid = 1;
247 val->no_cacheable = 0;
248 val->not_found = 0;
249
250 vvn = ngx_palloc(cf->pool, sizeof(ngx_http_variable_value_node_t));
251 if (vvn == NULL) {
252 return NGX_CONF_ERROR;
253 }
254
255 vvn->node.key = hash;
256 vvn->len = val->len;
257 vvn->value = val;
258
259 ngx_rbtree_insert(&ctx->rbtree, &vvn->node);
669 return NGX_CONF_ERROR;
260670 }
261671
262672 for (i = 2; i; i--) {
273683 /* rc == NGX_BUSY */
274684
275685 old = (ngx_http_variable_value_t *)
276 ngx_radix32tree_find(ctx->tree, cidrin.addr & cidrin.mask);
686 ngx_radix32tree_find(ctx->tree, cidrin.addr & cidrin.mask);
277687
278688 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
279 "duplicate parameter \"%V\", value: \"%v\", old value: \"%v\"",
280 &value[0], val, old);
689 "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
690 net, val, old);
281691
282692 rc = ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask);
283693
284694 if (rc == NGX_ERROR) {
695 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
285696 return NGX_CONF_ERROR;
286697 }
287698 }
288699
289700 return NGX_CONF_ERROR;
290701 }
702
703
704 static ngx_http_variable_value_t *
705 ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
706 ngx_str_t *value)
707 {
708 uint32_t hash;
709 ngx_http_variable_value_t *val;
710 ngx_http_variable_value_node_t *vvn;
711
712 hash = ngx_crc32_long(value->data, value->len);
713
714 val = ngx_http_variable_value_lookup(&ctx->rbtree, value, hash);
715
716 if (val) {
717 return val;
718 }
719
720 val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
721 if (val == NULL) {
722 return NULL;
723 }
724
725 val->len = value->len;
726 val->data = ngx_pstrdup(ctx->pool, value);
727 if (val->data == NULL) {
728 return NULL;
729 }
730
731 val->valid = 1;
732 val->no_cacheable = 0;
733 val->not_found = 0;
734
735 vvn = ngx_palloc(ctx->temp_pool, sizeof(ngx_http_variable_value_node_t));
736 if (vvn == NULL) {
737 return NULL;
738 }
739
740 vvn->node.key = hash;
741 vvn->len = val->len;
742 vvn->value = val;
743
744 ngx_rbtree_insert(&ctx->rbtree, &vvn->node);
745
746 return val;
747 }