From fb5d412a6a9813e1486b7a411ec6b5297209ca51 Mon Sep 17 00:00:00 2001
From: shennetsind <ind@henn.et>
Date: Sun, 18 Jan 2015 13:00:46 -0200
Subject: Connection History Redesign

Drops ~250kb in memory usage from each server, speeds up lookup/insertion and the cleanup timer will iterate thru the list rather than that ~65k array loop

Signed-off-by: shennetsind <ind@henn.et>
---
 src/common/socket.c | 137 +++++++++++++++++++++-------------------------------
 1 file changed, 56 insertions(+), 81 deletions(-)

(limited to 'src')

diff --git a/src/common/socket.c b/src/common/socket.c
index 9c1f35eee..600666d21 100644
--- a/src/common/socket.c
+++ b/src/common/socket.c
@@ -918,7 +918,6 @@ int do_sockets(int next)
 // IP rules and DDoS protection
 
 typedef struct connect_history {
-	struct connect_history* next;
 	uint32 ip;
 	int64 tick;
 	int count;
@@ -945,9 +944,7 @@ static int access_debug    = 0;
 static int ddos_count      = 10;
 static int ddos_interval   = 3*1000;
 static int ddos_autoreset  = 10*60*1000;
-/// Connection history, an array of linked lists.
-/// The array's index for any ip is ip&0xFFFF
-static ConnectHistory* connect_history[0x10000];
+DBMap *connect_history = NULL;
 
 static int connect_check_(uint32 ip);
 
@@ -967,7 +964,7 @@ static int connect_check(uint32 ip)
 ///  1 or 2 : Connection Accepted
 static int connect_check_(uint32 ip)
 {
-	ConnectHistory* hist = connect_history[ip&0xFFFF];
+	ConnectHistory* hist = NULL;
 	int i;
 	int is_allowip = 0;
 	int is_denyip = 0;
@@ -1004,99 +1001,86 @@ static int connect_check_(uint32 ip)
 	//  1 : Accept
 	//  2 : Unconditional Accept (accepts even if flagged as DDoS)
 	switch(access_order) {
-	case ACO_DENY_ALLOW:
-	default:
-		if( is_denyip )
-			connect_ok = 0; // Reject
-		else if( is_allowip )
-			connect_ok = 2; // Unconditional Accept
-		else
-			connect_ok = 1; // Accept
-		break;
-	case ACO_ALLOW_DENY:
-		if( is_allowip )
-			connect_ok = 2; // Unconditional Accept
-		else if( is_denyip )
-			connect_ok = 0; // Reject
-		else
-			connect_ok = 1; // Accept
-		break;
-	case ACO_MUTUAL_FAILURE:
-		if( is_allowip && !is_denyip )
-			connect_ok = 2; // Unconditional Accept
-		else
-			connect_ok = 0; // Reject
-		break;
+		case ACO_DENY_ALLOW:
+		default:
+			if( is_denyip )
+				connect_ok = 0; // Reject
+			else if( is_allowip )
+				connect_ok = 2; // Unconditional Accept
+			else
+				connect_ok = 1; // Accept
+			break;
+		case ACO_ALLOW_DENY:
+			if( is_allowip )
+				connect_ok = 2; // Unconditional Accept
+			else if( is_denyip )
+				connect_ok = 0; // Reject
+			else
+				connect_ok = 1; // Accept
+			break;
+		case ACO_MUTUAL_FAILURE:
+			if( is_allowip && !is_denyip )
+				connect_ok = 2; // Unconditional Accept
+			else
+				connect_ok = 0; // Reject
+			break;
 	}
 
 	// Inspect connection history
-	while( hist ) {
-		if( ip == hist->ip )
-		{// IP found
-			if( hist->ddos )
-			{// flagged as DDoS
-				return (connect_ok == 2 ? 1 : 0);
-			} else if( DIFF_TICK(timer->gettick(),hist->tick) < ddos_interval )
-			{// connection within ddos_interval
+	if( ( hist = uidb_get(connect_history, ip)) ) { //IP found
+		if( hist->ddos ) {// flagged as DDoS
+			return (connect_ok == 2 ? 1 : 0);
+		} else if( DIFF_TICK(timer->gettick(),hist->tick) < ddos_interval ) {// connection within ddos_interval
 				hist->tick = timer->gettick();
-				if( hist->count++ >= ddos_count )
-				{// DDoS attack detected
+				if( ++hist->count >= ddos_count ) {// DDoS attack detected
 					hist->ddos = 1;
 					ShowWarning("connect_check: DDoS Attack detected from %d.%d.%d.%d!\n", CONVIP(ip));
 					return (connect_ok == 2 ? 1 : 0);
 				}
 				return connect_ok;
-			} else
-			{// not within ddos_interval, clear data
-				hist->tick  = timer->gettick();
-				hist->count = 0;
-				return connect_ok;
-			}
+		} else {// not within ddos_interval, clear data
+			hist->tick  = timer->gettick();
+			hist->count = 0;
+			return connect_ok;
 		}
-		hist = hist->next;
 	}
 	// IP not found, add to history
 	CREATE(hist, ConnectHistory, 1);
-	memset(hist, 0, sizeof(ConnectHistory));
 	hist->ip   = ip;
 	hist->tick = timer->gettick();
-	hist->next = connect_history[ip&0xFFFF];
-	connect_history[ip&0xFFFF] = hist;
+	uidb_put(connect_history, ip, hist);
 	return connect_ok;
 }
 
 /// Timer function.
 /// Deletes old connection history records.
 static int connect_check_clear(int tid, int64 tick, int id, intptr_t data) {
-	int i;
 	int clear = 0;
 	int list  = 0;
-	ConnectHistory root;
-	ConnectHistory* prev_hist;
-	ConnectHistory* hist;
-
-	for( i=0; i < 0x10000 ; ++i ){
-		prev_hist = &root;
-		root.next = hist = connect_history[i];
-		while( hist ){
-			if( (!hist->ddos && DIFF_TICK(tick,hist->tick) > ddos_interval*3) ||
-					(hist->ddos && DIFF_TICK(tick,hist->tick) > ddos_autoreset) )
+	ConnectHistory *hist = NULL;
+	DBIterator *iter;
+	
+	if( !db_size(connect_history) )
+		return 0;
+	
+	iter = db_iterator(connect_history);
+	
+	for( hist = dbi_first(iter); dbi_exists(iter); hist = dbi_next(iter) ){
+		if( (!hist->ddos && DIFF_TICK(tick,hist->tick) > ddos_interval*3) ||
+			(hist->ddos && DIFF_TICK(tick,hist->tick) > ddos_autoreset) )
 			{// Remove connection history
-				prev_hist->next = hist->next;
-				aFree(hist);
-				hist = prev_hist->next;
+				uidb_remove(connect_history, hist->ip);
 				clear++;
-			} else {
-				prev_hist = hist;
-				hist = hist->next;
 			}
-			list++;
-		}
-		connect_history[i] = root.next;
-	}
+		list++;
+ 	}
+	
+	dbi_destroy(iter);
+	
 	if( access_debug ){
 		ShowInfo("connect_check_clear: Cleared %d of %d from IP list.\n", clear, list);
 	}
+	
 	return list;
 }
 
@@ -1220,17 +1204,8 @@ void socket_final(void)
 {
 	int i;
 #ifndef MINICORE
-	ConnectHistory* hist;
-	ConnectHistory* next_hist;
-
-	for( i=0; i < 0x10000; ++i ){
-		hist = connect_history[i];
-		while( hist ){
-			next_hist = hist->next;
-			aFree(hist);
-			hist = next_hist;
-		}
-	}
+	if( connect_history )
+		db_destroy(connect_history);
 	if( access_allow )
 		aFree(access_allow);
 	if( access_deny )
@@ -1422,7 +1397,7 @@ void socket_init(void)
 
 #ifndef MINICORE
 	// Delete old connection history every 5 minutes
-	memset(connect_history, 0, sizeof(connect_history));
+	connect_history = uidb_alloc(DB_OPT_RELEASE_DATA);
 	timer->add_func_list(connect_check_clear, "connect_check_clear");
 	timer->add_interval(timer->gettick()+1000, connect_check_clear, 0, 0, 5*60*1000);
 #endif
-- 
cgit v1.2.3-70-g09d2