Skip to content

Commit

Permalink
Add 'analyze' command to analyze bandwidth usage
Browse files Browse the repository at this point in the history
  • Loading branch information
bitbrute committed Oct 21, 2019
1 parent f3da51f commit 72e81fa
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 19 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ Type ```evillimiter``` or ```python3 bin/evillimiter``` to run the tool.
| ```block [ID1,ID2,...] (--upload) (--download)``` | Blocks internet connection of host(s) associated to specified ID.<br>```--upload``` limits outgoing traffic only <br>```--download``` limits incoming traffic only.
| ```free [ID1,ID2,...]``` | Unlimits/Unblocks host(s) associated to specified ID. Removes all further restrictions.
| ```add [IP] (--mac [MAC])``` | Adds custom host to host list. MAC-Address will be resolved automatically or can be specified manually.<br>For example: ```add 192.168.178.24``` or ```add 192.168.1.50 --mac 1c:fc:bc:2d:a6:37```
| ```monitor (--interval [time in ms])``` | Monitors bandwidth usage of limited hosts (current usage, total bandwidth used, ...).<br>```--interval``` sets the interval in milliseconds after bandwidth information get refreshed (default 500ms)<br>For example: monitor --interval 1000
| ```monitor (--interval [time in ms])``` | Monitors bandwidth usage of limited host(s) (current usage, total bandwidth used, ...).<br>```--interval``` sets the interval after bandwidth information get refreshed in milliseconds (default 500ms).<br>For example: ```monitor --interval 1000```
| ```analyze [ID1,ID2,...] (--duration [time in s])``` | Analyzes traffic of host(s) without limiting to determine who uses how much bandwidth.<br>```--duration``` specifies the duration of the analysis in seconds (default 30s).<br>For example: ```analyze 2,3 --duration 120```
| ```clear``` | Clears the terminal window.
| ```quit``` | Quits the application.
| ```?```, ```help``` | Displays command information similar to this one.
Expand Down
38 changes: 38 additions & 0 deletions evillimiter/console/chart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from evillimiter.console.io import IO


class BarChart(object):
def __init__(self, draw_char='▇', max_bar_length=30):
self.draw_char = draw_char
self.max_bar_length = max_bar_length

self._data = []

def add_value(self, value, prefix, suffix=''):
self._data.append({ 'value': value, 'prefix': prefix, 'suffix': suffix })

def get(self, reverse=False):
def remap(n, old_min, old_max, new_min, new_max):
return (((n - old_min) * (new_max - new_min)) / (old_max - old_min)) + new_min

self._data.sort(reverse=reverse, key=lambda x: x['value'])

max_value = self._data[0]['value'] if reverse else self._data[-1]['value']
max_prefix_length = max([len(x['prefix']) for x in self._data]) + 1

chart = ''

for value in self._data:
if max_value == 0:
bar_length = 0
else:
bar_length = round(remap(value['value'], 0, max_value, 0, self.max_bar_length))

chart += '{}{}: {} {}\n'.format(
value['prefix'],
' ' * (max_prefix_length - len(value['prefix'])),
self.draw_char * bar_length,
value['suffix']
)

return chart[:-1]
106 changes: 93 additions & 13 deletions evillimiter/menus/main_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .menu import CommandMenu
from evillimiter.networking.utils import BitRate
from evillimiter.console.io import IO
from evillimiter.console.chart import BarChart
from evillimiter.console.banner import get_main_banner
from evillimiter.networking.host import Host
from evillimiter.networking.limit import Limiter, Direction
Expand Down Expand Up @@ -50,6 +51,10 @@ def __init__(self, version, interface, gateway_ip, gateway_mac, netmask):
monitor_parser = self.parser.add_subparser('monitor', self._monitor_handler)
monitor_parser.add_parameterized_flag('--interval', 'interval')

analyze_parser = self.parser.add_subparser('analyze', self._analyze_handler)
analyze_parser.add_parameter('id')
analyze_parser.add_parameterized_flag('--duration', 'duration')

self.parser.add_subparser('help', self._help_handler)
self.parser.add_subparser('?', self._help_handler)

Expand Down Expand Up @@ -132,9 +137,9 @@ def _hosts_handler(self, args):
'{}Status{}'.format(IO.Style.BRIGHT, IO.Style.RESET_ALL)
]]

for i, host in enumerate(self.hosts):
for host in self.hosts:
table_data.append([
'{}{}{}'.format(IO.Fore.LIGHTYELLOW_EX, i, IO.Style.RESET_ALL),
'{}{}{}'.format(IO.Fore.LIGHTYELLOW_EX, self._get_host_id(host), IO.Style.RESET_ALL),
host.ip,
host.mac,
host.name,
Expand All @@ -157,6 +162,8 @@ def _limit_handler(self, args):
Limits bandwith of host to specified rate
"""
hosts = self._get_hosts_by_ids(args.id)
if hosts is None or len(hosts) == 0:
return

try:
rate = BitRate.from_rate_string(args.rate)
Expand All @@ -166,14 +173,12 @@ def _limit_handler(self, args):

direction = self._parse_direction_args(args)

if hosts is not None and len(hosts) > 0:
for host in hosts:
if not host.spoofed:
self.arp_spoofer.add(host)
for host in hosts:
self.arp_spoofer.add(host)
self.limiter.limit(host, direction, rate)
self.bandwidth_monitor.add(host)

self.limiter.limit(host, direction, rate)
self.bandwidth_monitor.add(host)
IO.ok('{}{}{r} {} {}limited{r} to {}.'.format(IO.Fore.LIGHTYELLOW_EX, host.ip, Direction.pretty_direction(direction), IO.Fore.LIGHTRED_EX, rate, r=IO.Style.RESET_ALL))
IO.ok('{}{}{r} {} {}limited{r} to {}.'.format(IO.Fore.LIGHTYELLOW_EX, host.ip, Direction.pretty_direction(direction), IO.Fore.LIGHTRED_EX, rate, r=IO.Style.RESET_ALL))

def _block_handler(self, args):
"""
Expand Down Expand Up @@ -271,9 +276,9 @@ def display(stdscr, interval):
y_off += 2
x_off = x_rst

for i, (host, result) in enumerate(host_results):
for host, result in host_results:
result_data = [
str(i),
str(self._get_host_id(host)),
host.ip,
host.name,
'{}↑ {}↓'.format(result.upload_rate, result.download_rate),
Expand Down Expand Up @@ -316,6 +321,71 @@ def display(stdscr, interval):
except curses.error:
IO.error('monitor error occurred. maybe terminal too small?')

def _analyze_handler(self, args):
hosts = self._get_hosts_by_ids(args.id)
if hosts is None or len(hosts) == 0:
return

duration = 30 # in s
if args.duration:
if not args.duration.isdigit():
IO.error('invalid duration.')
return

duration = int(args.duration)

hosts_to_be_freed = set()
host_values = {}

for host in hosts:
if not host.spoofed:
hosts_to_be_freed.add(host)

self.arp_spoofer.add(host)
self.bandwidth_monitor.add(host)

host_result = self.bandwidth_monitor.get(host)
host_values[host] = {}
host_values[host]['prev'] = (host_result.upload_total_size, host_result.download_total_size)

IO.ok('analyzing traffic for {}s.'.format(duration))
time.sleep(duration)

for host in hosts:
host_result = self.bandwidth_monitor.get(host)
host_values[host]['current'] = (host_result.upload_total_size, host_result.download_total_size)

IO.ok('cleaning up...')
for host in hosts_to_be_freed:
self._free_host(host)

upload_chart = BarChart(max_bar_length=29)
download_chart = BarChart(max_bar_length=29)

for host in hosts:
upload_value = host_values[host]['current'][0] - host_values[host]['prev'][0]
download_value = host_values[host]['current'][1] - host_values[host]['prev'][1]

prefix = '{}{}{} ({}, {})'.format(
IO.Fore.LIGHTYELLOW_EX, self._get_host_id(host), IO.Style.RESET_ALL,
host.ip,
host.name
)

upload_chart.add_value(upload_value.value, prefix, upload_value)
download_chart.add_value(download_value.value, prefix, download_value)

upload_table = SingleTable([[upload_chart.get()]], 'Upload')
download_table = SingleTable([[download_chart.get()]], 'Download')

upload_table.inner_heading_row_border = False
download_table.inner_heading_row_border = False

IO.spacer()
IO.print(upload_table.table)
IO.print(download_table.table)
IO.spacer()

def _clear_handler(self, args):
"""
Handler for the 'clear' command-line argument
Expand Down Expand Up @@ -361,9 +431,13 @@ def _help_handler(self, args):
{b}{s}e.g.: add 192.168.178.24
{s} add 192.168.1.50 --mac 1c:fc:bc:2d:a6:37{r}
{y}monitor (--interval [time in ms]){r}{}monitors bandwidth usage of limited hosts.
{y}monitor (--interval [time in ms]){r}{}monitors bandwidth usage of limited host(s).
{b}{s}e.g.: monitor --interval 600{r}
{y}analyze [ID1,ID2,...]{r}{}analyzes traffic of host(s) without limiting
{y} (--duration [time in s]){r}{}to determine who uses how much bandwidth.
{b}{s}e.g.: analyze 2,3 --duration 120{r}
{y}clear{r}{}clears the terminal window.
{y}quit{r}{}quits the application.
Expand All @@ -377,6 +451,8 @@ def _help_handler(self, args):
spaces[len('free [ID1,ID2,...]'):],
spaces[len('add [IP] (--mac [MAC])'):],
spaces[len('monitor (--interval [time in ms])'):],
spaces[len('analyze [ID1,ID2,...]'):],
spaces[len(' (--duration [time in s])'):],
spaces[len('clear'):],
spaces[len('quit'):],
y=IO.Fore.LIGHTYELLOW_EX, r=IO.Style.RESET_ALL, b=IO.Style.BRIGHT,
Expand All @@ -388,6 +464,11 @@ def _quit_handler(self, args):
self.interrupt_handler(False)
self.stop()

def _get_host_id(self, host):
for i, host_ in enumerate(self.hosts):
if host_ == host:
return i

def _print_help_reminder(self):
IO.print('type {Y}help{R} or {Y}?{R} to show command information.'.format(Y=IO.Fore.LIGHTYELLOW_EX, R=IO.Style.RESET_ALL))

Expand Down Expand Up @@ -444,4 +525,3 @@ def _free_host(self, host):
self.arp_spoofer.remove(host)
self.limiter.unlimit(host, Direction.BOTH)
self.bandwidth_monitor.remove(host)
IO.ok('{}{}{} freed.'.format(IO.Fore.LIGHTYELLOW_EX, host.ip, IO.Style.RESET_ALL))
2 changes: 1 addition & 1 deletion evillimiter/networking/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def __init__(self, ip, mac, name):
self.blocked = False

def __eq__(self, other):
return self.mac == other.mac or self.ip == other.ip
return self.ip == other.ip

def __hash__(self):
return hash((self.mac, self.ip))
Expand Down
3 changes: 3 additions & 0 deletions evillimiter/networking/limit.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ def block(self, host, direction):
self.host_ids_dict[host] = host_ids

def unlimit(self, host, direction):
if not host.limited:
return

host_ids = self.host_ids_dict[host]

if (direction & Direction.OUTGOING) == Direction.OUTGOING:
Expand Down
13 changes: 9 additions & 4 deletions evillimiter/networking/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def __str__(self):
raise Exception('Bitrate limit exceeded')

def __mul__(self, other):
if other is BitRate:
if isinstance(other, BitRate):
return BitRate(int(self.rate * other.rate))
return BitRate(int(self.rate * other))

Expand Down Expand Up @@ -216,17 +216,22 @@ def __int__(self):
return self.value

def __add__(self, other):
if other is ByteValue:
if isinstance(other, ByteValue):
return ByteValue(int(self.value + other.value))
return ByteValue(int(self.value + other))

def __sub__(self, other):
if isinstance(other, ByteValue):
return ByteValue(int(self.value - other.value))
return ByteValue(int(self.value - other))

def __mul__(self, other):
if other is ByteValue:
if isinstance(other, ByteValue):
return ByteValue(int(self.value * other.value))
return ByteValue(int(self.value * other))

def __ge__(self, other):
if other is ByteValue:
if isinstance(other, ByteValue):
return self.value >= other.value
return self.value >= other

Expand Down

0 comments on commit 72e81fa

Please sign in to comment.