Nginx的geo模块

1、相关配置

geo模块配置指令如下:

1
2
3
4
5

geo $remote_addr $var {

}

详细用法参考nginx官方文档

2、源码详解

2.1 首先来看看geo指令配置解析函数,这个函数的主要功能就是解析geo指令之后的变量,然后再调用ngx_http_geo解析{}中的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
static char *
ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *rv;
size_t len;
ngx_str_t *value, name;
ngx_uint_t i;
ngx_conf_t save;
ngx_pool_t *pool;
ngx_array_t *a;
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;

geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t));
if (geo == NULL) {
return NGX_CONF_ERROR;
}

name = value[1];

if (name.data[0] != '$') {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid variable name \"%V\"", &name);
return NGX_CONF_ERROR;
}

name.len--;
name.data++;

if (cf->args->nelts == 3) {

geo->index = ngx_http_get_variable_index(cf, &name);
if (geo->index == NGX_ERROR) {
return NGX_CONF_ERROR;
}

name = value[2];

if (name.data[0] != '$') {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid variable name \"%V\"", &name);
return NGX_CONF_ERROR;
}

name.len--;
name.data++;

} else {
geo->index = -1;
}

var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
if (var == NULL) {
return NGX_CONF_ERROR;
}

pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
if (pool == NULL) {
return NGX_CONF_ERROR;
}

ngx_memzero(&ctx, sizeof(ngx_http_geo_conf_ctx_t));

ctx.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
if (ctx.temp_pool == NULL) {
return NGX_CONF_ERROR;
}

ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value);

ctx.pool = cf->pool;
ctx.data_size = sizeof(ngx_http_geo_header_t)
+ sizeof(ngx_http_variable_value_t)
+ 0x10000 * sizeof(ngx_http_geo_range_t *);
ctx.allow_binary_include = 1;

save = *cf;
cf->pool = pool;
cf->ctx = &ctx;
cf->handler = ngx_http_geo;
cf->handler_conf = conf;

rv = ngx_conf_parse(cf, NULL);

*cf = save;

geo->proxies = ctx.proxies;
geo->proxy_recursive = ctx.proxy_recursive;

if (ctx.ranges) {

if (ctx.high.low && !ctx.binary_include) {
for (i = 0; i < 0x10000; i++) {
a = (ngx_array_t *) ctx.high.low[i];

if (a == NULL || a->nelts == 0) {
continue;
}

len = a->nelts * sizeof(ngx_http_geo_range_t);

ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *));
if (ctx.high.low[i] == NULL) {
return NGX_CONF_ERROR;
}

ngx_memcpy(ctx.high.low[i], a->elts, len);
ctx.high.low[i][a->nelts].value = NULL;
ctx.data_size += len + sizeof(void *);
}

if (ctx.allow_binary_include
&& !ctx.outside_entries
&& ctx.entries > 100000
&& ctx.includes == 1)
{
ngx_http_geo_create_binary_base(&ctx);
}
}

if (ctx.high.default_value == NULL) {
ctx.high.default_value = &ngx_http_variable_null_value;
}

geo->u.high = ctx.high;

var->get_handler = ngx_http_geo_range_variable;
var->data = (uintptr_t) geo;

ngx_destroy_pool(ctx.temp_pool);
ngx_destroy_pool(pool);

} else {
if (ctx.tree == NULL) {
ctx.tree = ngx_radix_tree_create(cf->pool, -1);
if (ctx.tree == NULL) {
return NGX_CONF_ERROR;
}
}

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;

ngx_destroy_pool(ctx.temp_pool);
ngx_destroy_pool(pool);

if (ngx_radix32tree_insert(ctx.tree, 0, 0,
(uintptr_t) &ngx_http_variable_null_value)
== NGX_ERROR)
{
return NGX_CONF_ERROR;
}

/* 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;
}

2.2 ngx_http_geo解析{}中的内容,处理ranges等指令和IP地址段或者CIDR地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

static char *
ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
{
char *rv;
ngx_str_t *value;
ngx_cidr_t cidr;
ngx_http_geo_conf_ctx_t *ctx;

ctx = cf->ctx;

value = cf->args->elts;

if (cf->args->nelts == 1) {

if (ngx_strcmp(value[0].data, "ranges") == 0) {

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");
goto failed;
}

ctx->ranges = 1;

rv = NGX_CONF_OK;

goto done;
}

else if (ngx_strcmp(value[0].data, "proxy_recursive") == 0) {
ctx->proxy_recursive = 1;
rv = NGX_CONF_OK;
goto done;
}
}

if (cf->args->nelts != 2) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid number of the geo parameters");
goto failed;
}

if (ngx_strcmp(value[0].data, "include") == 0) {

rv = ngx_http_geo_include(cf, ctx, &value[1]);

goto done;

} else if (ngx_strcmp(value[0].data, "proxy") == 0) {

if (ngx_http_geo_cidr_value(cf, &value[1], &cidr) != NGX_OK) {
goto failed;
}

rv = ngx_http_geo_add_proxy(cf, ctx, &cidr);

goto done;
}

if (ctx->ranges) {
rv = ngx_http_geo_range(cf, ctx, value);

} else {
rv = ngx_http_geo_cidr(cf, ctx, value);
}

done:

ngx_reset_pool(cf->pool);

return rv;

failed:

ngx_reset_pool(cf->pool);

return NGX_CONF_ERROR;
}


2.3 处理range地址,调用ngx_http_geo_range函数实现,接下来看看这个函数的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

static char *
ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
ngx_str_t *value)
{
u_char *p, *last;
in_addr_t start, end;
ngx_str_t *net;
ngx_uint_t del;

if (ngx_strcmp(value[0].data, "default") == 0) {

if (ctx->high.default_value) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"duplicate default geo range value: \"%V\", old value: \"%v\"",
&value[1], ctx->high.default_value);
}

ctx->high.default_value = ngx_http_geo_value(cf, ctx, &value[1]);
if (ctx->high.default_value == NULL) {
return NGX_CONF_ERROR;
}

return NGX_CONF_OK;
}

if (ctx->binary_include) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"binary geo range base \"%s\" cannot be mixed with usual entries",
ctx->include_name.data);
return NGX_CONF_ERROR;
}

if (ctx->high.low == NULL) {
ctx->high.low = ngx_pcalloc(ctx->pool,
0x10000 * sizeof(ngx_http_geo_range_t *));
if (ctx->high.low == NULL) {
return NGX_CONF_ERROR;
}
}

ctx->entries++;
ctx->outside_entries = 1;

if (ngx_strcmp(value[0].data, "delete") == 0) {
net = &value[1];
del = 1;

} else {
net = &value[0];
del = 0;
}

last = net->data + net->len;

p = ngx_strlchr(net->data, last, '-');

if (p == NULL) {
goto invalid;
}

start = ngx_inet_addr(net->data, p - net->data);

if (start == INADDR_NONE) {
goto invalid;
}

start = ntohl(start);

p++;

end = ngx_inet_addr(p, last - p);

if (end == INADDR_NONE) {
goto invalid;
}

end = ntohl(end);

if (start > end) {
goto invalid;
}

if (del) {
if (ngx_http_geo_delete_range(cf, ctx, start, end)) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"no address range \"%V\" to delete", net);
}

return NGX_CONF_OK;
}

ctx->value = ngx_http_geo_value(cf, ctx, &value[1]);

if (ctx->value == NULL) {
return NGX_CONF_ERROR;
}

ctx->net = net;

return ngx_http_geo_add_range(cf, ctx, start, end);

invalid:

ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net);

return NGX_CONF_ERROR;
}


2.4 处理CIDR地址,调用ngx_http_geo_cidr函数实现,接下来看看这个函数的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

static char *
ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
ngx_str_t *value)
{
char *rv;
ngx_int_t rc, del;
ngx_str_t *net;
ngx_cidr_t cidr;

if (ctx->tree == NULL) {
ctx->tree = ngx_radix_tree_create(ctx->pool, -1);
if (ctx->tree == NULL) {
return NGX_CONF_ERROR;
}
}

#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;
cidr.u.in.mask = 0;

rv = ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);

if (rv != NGX_CONF_OK) {
return rv;
}

#if (NGX_HAVE_INET6)
cidr.family = AF_INET6;
ngx_memzero(&cidr.u.in6, sizeof(ngx_in6_cidr_t));

rv = ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);

if (rv != NGX_CONF_OK) {
return rv;
}
#endif

return NGX_CONF_OK;
}

if (ngx_strcmp(value[0].data, "delete") == 0) {
net = &value[1];
del = 1;

} else {
net = &value[0];
del = 0;
}

if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) {
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);
}

if (del) {
switch (cidr.family) {

#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);
}

return NGX_CONF_OK;
}

return ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], net);
}

2.5 获取解析好的变量值,调用ngx_http_geo_range_variable函数实现,接下来看看这个函数的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

static ngx_int_t
ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;

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

#if (NGX_HAVE_UNIX_DOMAIN)
case AF_UNIX:
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) {
range = ctx->u.high.low[inaddr >> 16];

if (range) {
n = inaddr & 0xffff;
do {
if (n >= (ngx_uint_t) range->start
&& n <= (ngx_uint_t) range->end)
{
*v = *range->value;
break;
}
} while ((++range)->value);
}
}

ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http geo: %v", v);

return NGX_OK;
}


3、总结

以上就是整个geo模块的核心,stream下的geo模块实现方式类似。s