[nginx] Stream: connection limiting module.
Vladimir Homutov
vl at nginx.com
Thu Jun 18 11:18:40 UTC 2015
details: http://hg.nginx.org/nginx/rev/0dcef374b8bb
branches:
changeset: 6197:0dcef374b8bb
user: Vladimir Homutov <vl at nginx.com>
date: Thu Jun 18 14:17:30 2015 +0300
description:
Stream: connection limiting module.
stream {
limit_conn_zone $binary_remote_addr zone=perip:1m;
limit_conn_log_level error;
server {
...
limit_conn perip 1;
}
}
diffstat:
auto/modules | 5 +
auto/options | 4 +
auto/sources | 3 +
src/stream/ngx_stream.h | 1 +
src/stream/ngx_stream_handler.c | 9 +
src/stream/ngx_stream_limit_conn_module.c | 632 ++++++++++++++++++++++++++++++
6 files changed, 654 insertions(+), 0 deletions(-)
diffs (truncated from 722 to 300 lines):
diff -r c3ec43580a48 -r 0dcef374b8bb auto/modules
--- a/auto/modules Wed Jun 17 17:57:34 2015 +0300
+++ b/auto/modules Thu Jun 18 14:17:30 2015 +0300
@@ -514,6 +514,11 @@ if [ $STREAM = YES ]; then
STREAM_SRCS="$STREAM_SRCS $STREAM_SSL_SRCS"
fi
+ if [ $STREAM_LIMIT_CONN = YES ]; then
+ modules="$modules $STREAM_LIMIT_CONN_MODULE"
+ STREAM_SRCS="$STREAM_SRCS $STREAM_LIMIT_CONN_SRCS"
+ fi
+
if [ $STREAM_ACCESS = YES ]; then
modules="$modules $STREAM_ACCESS_MODULE"
STREAM_SRCS="$STREAM_SRCS $STREAM_ACCESS_SRCS"
diff -r c3ec43580a48 -r 0dcef374b8bb auto/options
--- a/auto/options Wed Jun 17 17:57:34 2015 +0300
+++ b/auto/options Thu Jun 18 14:17:30 2015 +0300
@@ -113,6 +113,7 @@ MAIL_SMTP=YES
STREAM=NO
STREAM_SSL=NO
+STREAM_LIMIT_CONN=YES
STREAM_ACCESS=YES
STREAM_UPSTREAM_HASH=YES
STREAM_UPSTREAM_LEAST_CONN=YES
@@ -283,6 +284,8 @@ use the \"--with-mail_ssl_module\" optio
--with-stream) STREAM=YES ;;
--with-stream_ssl_module) STREAM_SSL=YES ;;
+ --without-stream_limit_conn_module)
+ STREAM_LIMIT_CONN=NO ;;
--without-stream_access_module) STREAM_ACCESS=NO ;;
--without-stream_upstream_hash_module)
STREAM_UPSTREAM_HASH=NO ;;
@@ -452,6 +455,7 @@ cat << END
--with-stream enable TCP proxy module
--with-stream_ssl_module enable ngx_stream_ssl_module
+ --without-stream_limit_conn_module disable ngx_stream_limit_conn_module
--without-stream_access_module disable ngx_stream_access_module
--without-stream_upstream_hash_module
disable ngx_stream_upstream_hash_module
diff -r c3ec43580a48 -r 0dcef374b8bb auto/sources
--- a/auto/sources Wed Jun 17 17:57:34 2015 +0300
+++ b/auto/sources Thu Jun 18 14:17:30 2015 +0300
@@ -568,6 +568,9 @@ STREAM_SSL_MODULE="ngx_stream_ssl_module
STREAM_SSL_DEPS="src/stream/ngx_stream_ssl_module.h"
STREAM_SSL_SRCS="src/stream/ngx_stream_ssl_module.c"
+STREAM_LIMIT_CONN_MODULE=ngx_stream_limit_conn_module
+STREAM_LIMIT_CONN_SRCS=src/stream/ngx_stream_limit_conn_module.c
+
STREAM_ACCESS_MODULE=ngx_stream_access_module
STREAM_ACCESS_SRCS=src/stream/ngx_stream_access_module.c
diff -r c3ec43580a48 -r 0dcef374b8bb src/stream/ngx_stream.h
--- a/src/stream/ngx_stream.h Wed Jun 17 17:57:34 2015 +0300
+++ b/src/stream/ngx_stream.h Thu Jun 18 14:17:30 2015 +0300
@@ -118,6 +118,7 @@ typedef ngx_int_t (*ngx_stream_access_pt
typedef struct {
ngx_array_t servers; /* ngx_stream_core_srv_conf_t */
ngx_array_t listen; /* ngx_stream_listen_t */
+ ngx_stream_access_pt limit_conn_handler;
ngx_stream_access_pt access_handler;
} ngx_stream_core_main_conf_t;
diff -r c3ec43580a48 -r 0dcef374b8bb src/stream/ngx_stream_handler.c
--- a/src/stream/ngx_stream_handler.c Wed Jun 17 17:57:34 2015 +0300
+++ b/src/stream/ngx_stream_handler.c Thu Jun 18 14:17:30 2015 +0300
@@ -147,6 +147,15 @@ ngx_stream_init_connection(ngx_connectio
cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
+ if (cmcf->limit_conn_handler) {
+ rc = cmcf->limit_conn_handler(s);
+
+ if (rc != NGX_DECLINED) {
+ ngx_stream_close_connection(c);
+ return;
+ }
+ }
+
if (cmcf->access_handler) {
rc = cmcf->access_handler(s);
diff -r c3ec43580a48 -r 0dcef374b8bb src/stream/ngx_stream_limit_conn_module.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/stream/ngx_stream_limit_conn_module.c Thu Jun 18 14:17:30 2015 +0300
@@ -0,0 +1,632 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_stream.h>
+
+
+typedef struct {
+ u_char color;
+ u_char len;
+ u_short conn;
+ u_char data[1];
+} ngx_stream_limit_conn_node_t;
+
+
+typedef struct {
+ ngx_shm_zone_t *shm_zone;
+ ngx_rbtree_node_t *node;
+} ngx_stream_limit_conn_cleanup_t;
+
+
+typedef struct {
+ ngx_rbtree_t *rbtree;
+} ngx_stream_limit_conn_ctx_t;
+
+
+typedef struct {
+ ngx_shm_zone_t *shm_zone;
+ ngx_uint_t conn;
+} ngx_stream_limit_conn_limit_t;
+
+
+typedef struct {
+ ngx_array_t limits;
+ ngx_uint_t log_level;
+} ngx_stream_limit_conn_conf_t;
+
+
+static ngx_rbtree_node_t *ngx_stream_limit_conn_lookup(ngx_rbtree_t *rbtree,
+ ngx_str_t *key, uint32_t hash);
+static void ngx_stream_limit_conn_cleanup(void *data);
+static ngx_inline void ngx_stream_limit_conn_cleanup_all(ngx_pool_t *pool);
+
+static void *ngx_stream_limit_conn_create_conf(ngx_conf_t *cf);
+static char *ngx_stream_limit_conn_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static char *ngx_stream_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_stream_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static ngx_int_t ngx_stream_limit_conn_init(ngx_conf_t *cf);
+
+
+static ngx_conf_enum_t ngx_stream_limit_conn_log_levels[] = {
+ { ngx_string("info"), NGX_LOG_INFO },
+ { ngx_string("notice"), NGX_LOG_NOTICE },
+ { ngx_string("warn"), NGX_LOG_WARN },
+ { ngx_string("error"), NGX_LOG_ERR },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_command_t ngx_stream_limit_conn_commands[] = {
+
+ { ngx_string("limit_conn_zone"),
+ NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE2,
+ ngx_stream_limit_conn_zone,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("limit_conn"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2,
+ ngx_stream_limit_conn,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("limit_conn_log_level"),
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ offsetof(ngx_stream_limit_conn_conf_t, log_level),
+ &ngx_stream_limit_conn_log_levels },
+
+ ngx_null_command
+};
+
+
+static ngx_stream_module_t ngx_stream_limit_conn_module_ctx = {
+ ngx_stream_limit_conn_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_stream_limit_conn_create_conf, /* create server configuration */
+ ngx_stream_limit_conn_merge_conf, /* merge server configuration */
+};
+
+
+ngx_module_t ngx_stream_limit_conn_module = {
+ NGX_MODULE_V1,
+ &ngx_stream_limit_conn_module_ctx, /* module context */
+ ngx_stream_limit_conn_commands, /* module directives */
+ NGX_STREAM_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_stream_limit_conn_handler(ngx_stream_session_t *s)
+{
+ size_t n;
+ uint32_t hash;
+ ngx_str_t key;
+ ngx_uint_t i;
+ ngx_slab_pool_t *shpool;
+ ngx_rbtree_node_t *node;
+ ngx_pool_cleanup_t *cln;
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+ ngx_stream_limit_conn_ctx_t *ctx;
+ ngx_stream_limit_conn_node_t *lc;
+ ngx_stream_limit_conn_conf_t *lccf;
+ ngx_stream_limit_conn_limit_t *limits;
+ ngx_stream_limit_conn_cleanup_t *lccln;
+
+ switch (s->connection->sockaddr->sa_family) {
+
+ case AF_INET:
+ sin = (struct sockaddr_in *) s->connection->sockaddr;
+
+ key.len = sizeof(in_addr_t);
+ key.data = (u_char *) &sin->sin_addr;
+
+ break;
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) s->connection->sockaddr;
+
+ key.len = sizeof(struct in6_addr);
+ key.data = sin6->sin6_addr.s6_addr;
+
+ break;
+#endif
+
+ default:
+ return NGX_DECLINED;
+ }
+
+ hash = ngx_crc32_short(key.data, key.len);
+
+ lccf = ngx_stream_get_module_srv_conf(s, ngx_stream_limit_conn_module);
+ limits = lccf->limits.elts;
+
+ for (i = 0; i < lccf->limits.nelts; i++) {
+ ctx = limits[i].shm_zone->data;
+
+ shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr;
+
+ ngx_shmtx_lock(&shpool->mutex);
+
+ node = ngx_stream_limit_conn_lookup(ctx->rbtree, &key, hash);
+
+ if (node == NULL) {
+
+ n = offsetof(ngx_rbtree_node_t, color)
+ + offsetof(ngx_stream_limit_conn_node_t, data)
+ + key.len;
+
+ node = ngx_slab_alloc_locked(shpool, n);
+
+ if (node == NULL) {
+ ngx_shmtx_unlock(&shpool->mutex);
+ ngx_stream_limit_conn_cleanup_all(s->connection->pool);
+ return NGX_ABORT;
+ }
+
+ lc = (ngx_stream_limit_conn_node_t *) &node->color;
+
+ node->key = hash;
+ lc->len = (u_char) key.len;
+ lc->conn = 1;
+ ngx_memcpy(lc->data, key.data, key.len);
+
+ ngx_rbtree_insert(ctx->rbtree, node);
+
+ } else {
+
+ lc = (ngx_stream_limit_conn_node_t *) &node->color;
+
+ if ((ngx_uint_t) lc->conn >= limits[i].conn) {
+
+ ngx_shmtx_unlock(&shpool->mutex);
+
More information about the nginx-devel
mailing list