# $Id: synflood.bro,v 1.1 2003/08/31 00:40:21 vern Exp $

@load alert

redef enum Alert += {
	SynFloodStart,    # start of syn-flood against a certain victim
	SynFloodEnd,      # end of syn-flood against a certain victim
	SynFloodStatus,   # report of ongoing syn-flood
};

# We report a syn-flood if more than SYNFLOOD_THRESHOLD connections attempts
# have been reported within the last SYNFLOOD_INTERVAL for a certain IP.
# (We sample the attempts by SYNFLOOD_SAMPLE_RATE, so the attempt counter
# is an estimated value.). If a victim is identified, we install a filter
# via install_dst_filter and sample the packets targeting it by
# SYNFLOOD_VICTIM_SAMPLE_RATE.
#
# Ongoing syn-floods are reported every SYNFLOOD_REPORT_INTERVAL.

global SYNFLOOD_THRESHOLD = 10000 &redef;
global SYNFLOOD_INTERVAL = 10 secs &redef;
global SYNFLOOD_SAMPLE_RATE = 100 &redef;
global SYNFLOOD_VICTIM_SAMPLE_RATE = 100 &redef;
global SYNFLOOD_REPORT_INTERVAL = 5 mins &redef;

global conn_attempts: table[addr] of count &default = 0;
global victim_attempts: table[addr,addr] of count
	&default = 0  &read_expire = 5mins;

global current_victims: set[addr] &read_expire = 60mins;
global accumulated_conn_attempts: table[addr] of count &default = 0;

global sample_count = 0;

event connection_attempt(c: connection)
	{
	if ( ++sample_count % SYNFLOOD_SAMPLE_RATE == 0 )
		{
		local ip = c$id$resp_h;
		if ( ++conn_attempts[ip] * SYNFLOOD_SAMPLE_RATE == SYNFLOOD_THRESHOLD &&
		     ip !in current_victims )
			{
			ALERT([$alert=SynFloodStart, $src=ip,
				   $msg=fmt("start of syn-flood against %s; sampling packets now", ip)]);
			add current_victims[ip];
			install_dst_addr_filter(ip, 0, SYNFLOOD_VICTIM_SAMPLE_RATE);
			}
		}
	}

event check_synflood()
 	{
	for ( ip in current_victims )
		{
		accumulated_conn_attempts[ip] =
			accumulated_conn_attempts[ip] + conn_attempts[ip];

		if ( conn_attempts[ip] * SYNFLOOD_SAMPLE_RATE * SYNFLOOD_VICTIM_SAMPLE_RATE < SYNFLOOD_THRESHOLD )
			{
			ALERT([$alert=SynFloodEnd, $src=ip,
				   $msg=fmt("end of syn-flood against %s; stopping sampling", ip)]);
			delete current_victims[ip];
			uninstall_dst_addr_filter(ip);
			}
		}

	clear_table(conn_attempts);
	schedule SYNFLOOD_INTERVAL { check_synflood() };
	}

event report_synflood()
	{
	for ( ip in current_victims )
		{
		local est_num_conn = accumulated_conn_attempts[ip] *
			SYNFLOOD_SAMPLE_RATE * SYNFLOOD_VICTIM_SAMPLE_RATE;

		ALERT([$alert=SynFloodStatus, $src=ip,
			   $msg=fmt("syn-flood against %s; estimated %d connections attempts in last %s seconds",
						ip, est_num_conn,
						SYNFLOOD_REPORT_INTERVAL)]);
		}

	clear_table(accumulated_conn_attempts);

	schedule SYNFLOOD_REPORT_INTERVAL { report_synflood() };
	}

event bro_init()
	{
	schedule SYNFLOOD_INTERVAL { check_synflood() };
	schedule SYNFLOOD_REPORT_INTERVAL { report_synflood() };
	}
