summaryrefslogtreecommitdiff
path: root/accel-pppd/extra/connlimit.c
diff options
context:
space:
mode:
Diffstat (limited to 'accel-pppd/extra/connlimit.c')
-rw-r--r--accel-pppd/extra/connlimit.c169
1 files changed, 169 insertions, 0 deletions
diff --git a/accel-pppd/extra/connlimit.c b/accel-pppd/extra/connlimit.c
new file mode 100644
index 00000000..56a08954
--- /dev/null
+++ b/accel-pppd/extra/connlimit.c
@@ -0,0 +1,169 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+#include "ppp.h"
+#include "events.h"
+#include "triton.h"
+#include "log.h"
+
+#include "memdebug.h"
+
+struct item
+{
+ struct list_head entry;
+ uint64_t key;
+ struct timespec ts;
+ int count;
+};
+
+static int conf_burst = 3;
+static int conf_burst_timeout = 60 * 1000;
+static int conf_limit_timeout = 5000;
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+static LIST_HEAD(items);
+
+int __export connlimit_check(uint64_t key)
+{
+ struct item *it;
+ struct timespec ts;
+ unsigned int d;
+ struct list_head *pos, *n;
+ LIST_HEAD(tmp_list);
+ int r = 1;
+
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ pthread_mutex_lock(&lock);
+ log_debug("connlimit: check entry %llu\n", key);
+ list_for_each_safe(pos, n, &items) {
+ it = list_entry(pos, typeof(*it), entry);
+
+ d = (ts.tv_sec - it->ts.tv_sec) * 1000 + (ts.tv_nsec - it->ts.tv_nsec) / 1000000;
+
+ if (it->key == key) {
+ it->count++;
+ if (it->count >= conf_burst) {
+ if (d >= conf_limit_timeout) {
+ it->ts = ts;
+ list_move(&it->entry, &items);
+ r = 0;
+ } else
+ r = -1;
+ } else {
+ if (d >= conf_burst_timeout) {
+ it->ts = ts;
+ list_move(&it->entry, &items);
+ }
+ r = 0;
+ }
+ break;
+ }
+
+ if (d > conf_burst_timeout) {
+ log_debug("connlimit: remove %llu\n", it->key);
+ list_move(&it->entry, &tmp_list);
+ }
+ }
+ pthread_mutex_unlock(&lock);
+
+ if (r == 1) {
+ it = _malloc(sizeof(*it));
+ memset(it, 0, sizeof(*it));
+ it->ts = ts;
+ it->key = key;
+
+ log_debug("connlimit: add entry %llu\n", key);
+
+ pthread_mutex_lock(&lock);
+ list_add(&it->entry, &items);
+ pthread_mutex_unlock(&lock);
+
+ r = 0;
+ }
+
+ if (r == 0)
+ log_debug("connlimit: accept %llu\n", key);
+ else
+ log_debug("connlimit: drop %llu\n", key);
+
+
+ while (!list_empty(&tmp_list)) {
+ it = list_entry(tmp_list.next, typeof(*it), entry);
+ list_del(&it->entry);
+ _free(it);
+ }
+
+ return r;
+}
+
+static int parse_limit(const char *opt, int *limit, int *time)
+{
+ char *endptr;
+
+ *limit = strtol(opt, &endptr, 10);
+
+ if (!*endptr) {
+ *time = 1;
+ return 0;
+ }
+
+ if (*endptr != '/')
+ goto out_err;
+
+ opt = endptr + 1;
+ *time = strtol(opt, &endptr, 10);
+
+ if (endptr == opt)
+ *time = 1;
+
+ if (*endptr == 's')
+ return 0;
+
+ if (*endptr == 'm') {
+ *time *= 60;
+ return 0;
+ }
+
+ if (*endptr == 'h') {
+ *time *= 3600;
+ return 0;
+ }
+
+out_err:
+ log_error("connlimit: failed to parse '%s'\n", opt);
+ return -1;
+}
+
+static void load_config()
+{
+ const char *opt;
+ int n,t;
+
+ opt = conf_get_opt("connlimit", "limit");
+ if (opt) {
+ if (parse_limit(opt, &n, &t))
+ return;
+ conf_limit_timeout = t * 1000 / n;
+ }
+
+ opt = conf_get_opt("connlimit", "burst");
+ if (opt)
+ conf_burst = atoi(opt);
+
+ opt = conf_get_opt("connlimit", "timeout");
+ if (opt)
+ conf_burst_timeout = atoi(opt) * 1000;
+}
+
+static void init()
+{
+ load_config();
+
+ triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config);
+}
+
+DEFINE_INIT(200, init);