MEDIUM: listener: apply a limit on the session rate submitted to SSL
Just like the previous commit, we sometimes want to limit the rate of
incoming SSL connections. While it can be done for a frontend, it was
not possible for a whole process, which makes sense when multiple
processes are running on a system to server multiple customers.
The new global "maxsslrate" setting is usable to fix a limit on the
session rate going to the SSL frontends. The limits applies before
the SSL handshake and not after, so that it saves the SSL stack from
expensive key computations that would finally be aborted before being
accounted for.
The same setting may be changed at run time on the CLI using
"set rate-limit ssl-session global".
diff --git a/src/cfgparse.c b/src/cfgparse.c
index cab9d6e..4d1ecd0 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -888,6 +888,19 @@
}
global.sps_lim = atol(args[1]);
}
+ else if (!strcmp(args[0], "maxsslrate")) {
+ if (global.ssl_lim != 0) {
+ Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
+ err_code |= ERR_ALERT;
+ goto out;
+ }
+ if (*(args[1]) == 0) {
+ Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+ global.ssl_lim = atol(args[1]);
+ }
else if (!strcmp(args[0], "maxcomprate")) {
if (*(args[1]) == 0) {
Alert("parsing [%s:%d] : '%s' expects an integer argument in kb/s.\n", file, linenum, args[0]);
diff --git a/src/dumpstats.c b/src/dumpstats.c
index b6a4dc4..51b47ca 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -1462,6 +1462,45 @@
return 1;
}
}
+#ifdef USE_OPENSSL
+ else if (strcmp(args[2], "ssl-sessions") == 0) {
+ if (strcmp(args[3], "global") == 0) {
+ int v;
+
+ if (s->listener->bind_conf->level < ACCESS_LVL_ADMIN) {
+ appctx->ctx.cli.msg = stats_permission_denied_msg;
+ appctx->st0 = STAT_CLI_PRINT;
+ return 1;
+ }
+
+ if (!*args[4]) {
+ appctx->ctx.cli.msg = "Expects an integer value.\n";
+ appctx->st0 = STAT_CLI_PRINT;
+ return 1;
+ }
+
+ v = atoi(args[4]);
+ if (v < 0) {
+ appctx->ctx.cli.msg = "Value out of range.\n";
+ appctx->st0 = STAT_CLI_PRINT;
+ return 1;
+ }
+
+ global.ssl_lim = v;
+
+ /* Dequeues all of the listeners waiting for a resource */
+ if (!LIST_ISEMPTY(&global_listener_queue))
+ dequeue_all_listeners(&global_listener_queue);
+
+ return 1;
+ }
+ else {
+ appctx->ctx.cli.msg = "'set rate-limit ssl-sessions' only supports 'global'.\n";
+ appctx->st0 = STAT_CLI_PRINT;
+ return 1;
+ }
+ }
+#endif
else if (strcmp(args[2], "http-compression") == 0) {
if (strcmp(args[3], "global") == 0) {
int v;
@@ -1482,7 +1521,7 @@
}
}
else {
- appctx->ctx.cli.msg = "'set rate-limit' supports 'connections', 'sessions', and 'http-compression'.\n";
+ appctx->ctx.cli.msg = "'set rate-limit' supports 'connections', 'sessions', 'ssl-sessions', and 'http-compression'.\n";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
@@ -2223,6 +2262,11 @@
"SessRate: %d\n"
"SessRateLimit: %d\n"
"MaxSessRate: %d\n"
+#ifdef USE_OPENSSL
+ "SslRate: %d\n"
+ "SslRateLimit: %d\n"
+ "MaxSslRate: %d\n"
+#endif
"CompressBpsIn: %u\n"
"CompressBpsOut: %u\n"
"CompressBpsRateLim: %u\n"
@@ -2251,6 +2295,9 @@
global.maxpipes, pipes_used, pipes_free,
read_freq_ctr(&global.conn_per_sec), global.cps_lim, global.cps_max,
read_freq_ctr(&global.sess_per_sec), global.sps_lim, global.sps_max,
+#ifdef USE_OPENSSL
+ read_freq_ctr(&global.ssl_per_sec), global.ssl_lim, global.ssl_max,
+#endif
read_freq_ctr(&global.comp_bps_in), read_freq_ctr(&global.comp_bps_out),
global.comp_rate_lim,
#ifdef USE_ZLIB
diff --git a/src/listener.c b/src/listener.c
index 95a1199..1ce35de 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -294,7 +294,23 @@
if (max_accept > max)
max_accept = max;
}
+#ifdef USE_OPENSSL
+ if (!(l->options & LI_O_UNLIMITED) && global.ssl_lim && l->bind_conf && l->bind_conf->is_ssl) {
+ int max = freq_ctr_remain(&global.ssl_per_sec, global.ssl_lim, 0);
+ int expire;
+ if (unlikely(!max)) {
+ /* frontend accept rate limit was reached */
+ limit_listener(l, &global_listener_queue);
+ expire = tick_add(now_ms, next_event_delay(&global.ssl_per_sec, global.ssl_lim, 0));
+ task_schedule(global_listener_queue_task, tick_first(expire, global_listener_queue_task->expire));
+ return;
+ }
+
+ if (max_accept > max)
+ max_accept = max;
+ }
+#endif
if (p && p->fe_sps_lim) {
int max = freq_ctr_remain(&p->fe_sess_per_sec, p->fe_sps_lim, 0);
@@ -435,6 +451,14 @@
if (global.sess_per_sec.curr_ctr > global.sps_max)
global.sps_max = global.sess_per_sec.curr_ctr;
}
+#ifdef USE_OPENSSL
+ if (!(l->options & LI_O_UNLIMITED) && l->bind_conf && l->bind_conf->is_ssl) {
+
+ update_freq_ctr(&global.ssl_per_sec, 1);
+ if (global.ssl_per_sec.curr_ctr > global.ssl_max)
+ global.ssl_max = global.ssl_per_sec.curr_ctr;
+ }
+#endif
} /* end of while (max_accept--) */