evillimiter-dk 1.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. evillimiter_dk-1.6.0/LICENSE +22 -0
  2. evillimiter_dk-1.6.0/PKG-INFO +187 -0
  3. evillimiter_dk-1.6.0/README.md +146 -0
  4. evillimiter_dk-1.6.0/evillimiter/__init__.py +17 -0
  5. evillimiter_dk-1.6.0/evillimiter/common/__init__.py +0 -0
  6. evillimiter_dk-1.6.0/evillimiter/common/config.py +49 -0
  7. evillimiter_dk-1.6.0/evillimiter/common/globals.py +9 -0
  8. evillimiter_dk-1.6.0/evillimiter/console/__init__.py +0 -0
  9. evillimiter_dk-1.6.0/evillimiter/console/banner.py +18 -0
  10. evillimiter_dk-1.6.0/evillimiter/console/chart.py +38 -0
  11. evillimiter_dk-1.6.0/evillimiter/console/io.py +103 -0
  12. evillimiter_dk-1.6.0/evillimiter/console/shell.py +28 -0
  13. evillimiter_dk-1.6.0/evillimiter/evillimiter.py +188 -0
  14. evillimiter_dk-1.6.0/evillimiter/menus/__init__.py +0 -0
  15. evillimiter_dk-1.6.0/evillimiter/menus/main_menu.py +771 -0
  16. evillimiter_dk-1.6.0/evillimiter/menus/menu.py +49 -0
  17. evillimiter_dk-1.6.0/evillimiter/menus/parser.py +162 -0
  18. evillimiter_dk-1.6.0/evillimiter/networking/__init__.py +0 -0
  19. evillimiter_dk-1.6.0/evillimiter/networking/dhcp_listener.py +70 -0
  20. evillimiter_dk-1.6.0/evillimiter/networking/host.py +40 -0
  21. evillimiter_dk-1.6.0/evillimiter/networking/limit.py +289 -0
  22. evillimiter_dk-1.6.0/evillimiter/networking/monitor.py +93 -0
  23. evillimiter_dk-1.6.0/evillimiter/networking/ndp_spoof.py +129 -0
  24. evillimiter_dk-1.6.0/evillimiter/networking/scan.py +88 -0
  25. evillimiter_dk-1.6.0/evillimiter/networking/spoof.py +80 -0
  26. evillimiter_dk-1.6.0/evillimiter/networking/utils.py +444 -0
  27. evillimiter_dk-1.6.0/evillimiter/networking/watch.py +103 -0
  28. evillimiter_dk-1.6.0/evillimiter_dk.egg-info/PKG-INFO +187 -0
  29. evillimiter_dk-1.6.0/evillimiter_dk.egg-info/SOURCES.txt +44 -0
  30. evillimiter_dk-1.6.0/evillimiter_dk.egg-info/dependency_links.txt +1 -0
  31. evillimiter_dk-1.6.0/evillimiter_dk.egg-info/entry_points.txt +2 -0
  32. evillimiter_dk-1.6.0/evillimiter_dk.egg-info/requires.txt +6 -0
  33. evillimiter_dk-1.6.0/evillimiter_dk.egg-info/top_level.txt +1 -0
  34. evillimiter_dk-1.6.0/setup.cfg +4 -0
  35. evillimiter_dk-1.6.0/setup.py +102 -0
  36. evillimiter_dk-1.6.0/tests/test_config.py +87 -0
  37. evillimiter_dk-1.6.0/tests/test_dhcp_listener.py +69 -0
  38. evillimiter_dk-1.6.0/tests/test_host.py +114 -0
  39. evillimiter_dk-1.6.0/tests/test_io.py +64 -0
  40. evillimiter_dk-1.6.0/tests/test_limit.py +247 -0
  41. evillimiter_dk-1.6.0/tests/test_main_menu.py +177 -0
  42. evillimiter_dk-1.6.0/tests/test_monitor.py +40 -0
  43. evillimiter_dk-1.6.0/tests/test_ndp_spoof.py +131 -0
  44. evillimiter_dk-1.6.0/tests/test_scan.py +128 -0
  45. evillimiter_dk-1.6.0/tests/test_utils.py +265 -0
  46. evillimiter_dk-1.6.0/tests/test_watch.py +70 -0
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019-2026 bitbrute
4
+ Copyright (c) 2026 DavidsonRafaelK (fork-specific changes)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,187 @@
1
+ Metadata-Version: 2.4
2
+ Name: evillimiter-dk
3
+ Version: 1.6.0
4
+ Summary: Monitors, analyzes and limits the bandwidth of devices on the local network
5
+ Home-page: https://github.com/DavidsonRafaelK/evillimiter
6
+ Author: DavidsonRafaelK
7
+ Author-email: davidsonrafael20@gmail.com
8
+ License: MIT
9
+ Keywords: evillimiter,limit,bandwidth,network
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: End Users/Desktop
13
+ Classifier: Intended Audience :: System Administrators
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Natural Language :: English
16
+ Classifier: Operating System :: Unix
17
+ Classifier: Programming Language :: Python :: 3.7
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Topic :: System :: Networking
20
+ Requires-Python: >= 3
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: colorama
24
+ Requires-Dist: netaddr
25
+ Requires-Dist: netifaces
26
+ Requires-Dist: tqdm
27
+ Requires-Dist: scapy
28
+ Requires-Dist: terminaltables
29
+ Dynamic: author
30
+ Dynamic: author-email
31
+ Dynamic: classifier
32
+ Dynamic: description
33
+ Dynamic: description-content-type
34
+ Dynamic: home-page
35
+ Dynamic: keywords
36
+ Dynamic: license
37
+ Dynamic: license-file
38
+ Dynamic: requires-dist
39
+ Dynamic: requires-python
40
+ Dynamic: summary
41
+
42
+ <p align="center"><img src="https://i.imgur.com/CBGh0Yx.png" /></p>
43
+
44
+ # Evil Limiter
45
+
46
+ [![License Badge](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
47
+ [![Compatibility](https://img.shields.io/badge/python-3-brightgreen.svg)](PROJECT)
48
+ [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://GitHub.com/Naereen/StrapDown.js/graphs/commit-activity)
49
+ [![Open Source Love](https://badges.frapsoft.com/os/v3/open-source.svg?v=102)](https://github.com/ellerbrock/open-source-badge/)
50
+
51
+ > This project is a maintained fork of [bitbrute/evillimiter](https://github.com/bitbrute/evillimiter). The upstream repository is no longer maintained, so this fork continues development - bug fixes, new features, and compatibility updates. See [Fork-specific changes](#fork-specific-changes) below.
52
+
53
+ A tool to monitor, analyze and limit the bandwidth (upload/download) of devices on your local network without physical or administrative access.<br>
54
+ ```evillimiter``` employs [ARP spoofing](https://en.wikipedia.org/wiki/ARP_spoofing) and [traffic shaping](https://en.wikipedia.org/wiki/Traffic_shaping) to throttle the bandwidth of hosts on the network. On IPv6-enabled networks, [NDP spoofing](https://en.wikipedia.org/wiki/Address_Resolution_Protocol#Vulnerabilities) is used alongside ARP spoofing to also cut off a host's IPv6 traffic, which would otherwise bypass the IPv4-only limit/block.
55
+
56
+ **Searching for a Windows-compatible version?**<br>
57
+ Check out the open-source alternative [EvilLimiter for Windows](https://github.com/bitbrute/evillimiter-windows).
58
+
59
+ ## Requirements
60
+ - Linux distribution
61
+ - Python 3 or greater
62
+
63
+ Possibly missing python packages will be installed during the installation process.
64
+
65
+ #### Compatibility notes
66
+
67
+ ```evillimiter``` locates ```tc```, ```iptables``` and ```sysctl``` via ```PATH``` at startup and errors clearly if one is missing. It shells out to the ```iptables``` binary specifically - on distros where ```iptables``` is an ```iptables-nft``` compatibility shim this generally works, but a firewall managed purely through native ```nft``` rules with no ```iptables``` shim installed is not something this tool talks to.
68
+
69
+ ## Installation
70
+
71
+ ```bash
72
+ git clone https://github.com/DavidsonRafaelK/evillimiter.git
73
+ cd evillimiter
74
+ python3 -m venv .venv
75
+ source .venv/bin/activate
76
+ pip install .
77
+ ```
78
+
79
+ Run it with ```sudo``` (raw sockets, ```iptables```/```tc``` need root) - it's the run that needs root, not the install:
80
+
81
+ ```bash
82
+ sudo .venv/bin/evillimiter
83
+ ```
84
+
85
+ Note: ```sudo python3 setup.py install``` (the old instructions) is deprecated in modern setuptools and often fails outright with ```ModuleNotFoundError: No module named 'setuptools'``` - ```sudo``` runs the system Python, which usually doesn't have setuptools installed, unlike the venv's own Python that ```pip``` manages for you. On distros that enforce [PEP 668](https://peps.python.org/pep-0668/) (Arch, Debian 12+, ...), installing outside a venv is blocked entirely for this reason.
86
+
87
+ Alternatively, you can download a desired version from the [Release page](https://github.com/DavidsonRafaelK/evillimiter/releases).<br>
88
+
89
+ ## Usage
90
+
91
+ Type ```evillimiter``` or ```python3 bin/evillimiter``` to run the tool.
92
+
93
+ ```evillimiter``` will try to resolve required information (network interface, netmask, gateway address, ...) on its own, automatically.
94
+
95
+ <p align="center"><img src="https://raw.githubusercontent.com/DavidsonRafaelK/evillimiter/master/docs/images/screenshot.png" alt="evillimiter running in a terminal" width="800" /></p>
96
+
97
+ #### Command-Line Arguments
98
+
99
+ | Argument | Explanation |
100
+ | -------- | ----------- |
101
+ | ```-h``` | Displays help message listing all command-line arguments |
102
+ | ```-i [Interface Name]``` | Specifies network interface (resolved if not specified)|
103
+ | ```-g [Gateway IP Address]``` | Specifies gateway IP address (resolved if not specified)|
104
+ | ```-m [Gateway MAC Address]``` | Specifies gateway MAC address (resolved if not specified)|
105
+ | ```-n [Netmask Address]``` | Specifies netmask (resolved if not specified)|
106
+ | ```-f``` | Flushes current iptables and tc configuration. Ensures that packets are dealt with correctly.|
107
+ | ```--colorless``` | Disables colored output |
108
+ | ```-l [File Path]```, ```--log-file [File Path]``` | Also appends every ok/error message to this file (plain text, no color codes). |
109
+ | ```--version``` | Prints the installed version and exits. |
110
+
111
+ #### Config file
112
+
113
+ Any of the flags above (except ```-f```/```--flush```, which is a one-shot action, not a persisted preference) can be given a default in an optional ini file at ```~/.config/evillimiter/config.ini``` (or ```$XDG_CONFIG_HOME/evillimiter/config.ini```). A command-line flag always overrides the config file.
114
+
115
+ ```ini
116
+ [general]
117
+ interface = wlan0
118
+ colorless = true
119
+ log_file = /var/log/evillimiter.log
120
+
121
+ [watch]
122
+ interval = 30
123
+ range = 192.168.1.1-192.168.1.50
124
+ ```
125
+
126
+ ```[watch]``` sets the initial values normally set at runtime via ```watch set interval```/```watch set range``` (see below), so they don't need to be re-entered every session.
127
+
128
+ #### ```evillimiter``` Commands
129
+
130
+ | Command | Explanation |
131
+ | ------- | ----------- |
132
+ | ```scan (--range [IP Range])``` | Scans your network for online hosts. One of the first things to do after start.<br>```--range``` lets you specify a custom IP range.<br>For example: ```scan --range 192.168.178.1-192.168.178.40``` or just ```scan``` to scan the entire subnet.
133
+ | ```hosts (--force)``` | Displays all the hosts/devices previously scanned and basic information. Shows ID for each host that is required for interaction.<br>```--force``` forces the table to be shown, even when it doesn't fit the terminal.
134
+ | ```limit [ID1,ID2,...] [Rate] (--upload) (--download)``` | Limits bandwidth of host(s) associated to specified ID. Rate determines the internet speed. Host(s) are automatically added to the watchlist.<br>```--upload``` limits outgoing traffic only.<br>```--download``` limits incoming traffic only.<br>Valid rates: ```bit```, ```kbit```, ```mbit```, ```gbit```<br>For example: ```limit 4,5,6 200kbit``` or ```limit all 1gbit```
135
+ | ```block [ID1,ID2,...] (--upload) (--download)``` | Blocks internet connection of host(s) associated to specified ID. Host(s) are automatically added to the watchlist.<br>```--upload``` limits outgoing traffic only <br>```--download``` limits incoming traffic only.
136
+ | ```free [ID1,ID2,...]``` | Unlimits/Unblocks host(s) associated to specified ID. Removes all further restrictions.
137
+ | ```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```
138
+ | ```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```
139
+ | ```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```
140
+ | ```watch``` | Shows current watch status, including each watched host's Online/Offline state as of the last scan sweep. The watch feature detects when a host reconnects with a different IP address.<br>Hosts are added to the watchlist automatically upon ```limit``` or ```block```.
141
+ | ```watch add [ID1,ID2,...]``` | Adds specified host(s) to the watchlist.<br>For example: ```watch add 6,7,8```
142
+ | ```watch remove [ID1,ID2,...]``` | Removes specified host(s) from the watchlist.<br>For example: ```watch remove all```
143
+ | ```watch set [Attribute] [Value]``` | Changes current watch settings. The following attributes can be changed:<br>```range``` is the IP range to scan for reconnects.<br>```interval``` is the time to wait between each network scan (in seconds).<br>For example: ```watch set interval 120```
144
+ | ```clear``` | Clears the terminal window.
145
+ | ```quit``` | Quits the application.
146
+ | ```?```, ```help``` | Displays command information similar to this one.
147
+
148
+ ## Restrictions
149
+
150
+ - **Rate-limits IPv4 traffic only**, since [ARP spoofing](https://en.wikipedia.org/wiki/ARP_spoofing) requires the ARP packet that is only present on IPv4 networks. On networks with an IPv6 default route, a host's IPv6 traffic is instead fully blocked via NDP spoofing rather than rate-limited, since the ```tc```/```iptables``` rules that shape traffic are IPv4-only.
151
+ - **Cellular fallback defeats it.** A blocked/limited phone can just switch to LTE/5G once WiFi degrades (Android "avoid poor connections", iOS equivalents) - that traffic never touches your network at all.
152
+ - **MAC-randomizing devices reappear as a new host on reconnect.** Many phones present a different MAC per network join by default, so ```watch``` (matches by MAC) can't follow them; the old restriction is left bound to a MAC/IP nobody uses anymore.
153
+ - **Already-open connections can straggle.** A stream/download in progress doesn't always re-resolve the gateway's address mid-flight, so it can keep flowing on a stale ARP/NDP cache entry until it naturally resets - new connections are caught immediately.
154
+ - **Devices on a different subnet/VLAN are invisible.** Band-steering mesh systems that split 2.4GHz/5GHz onto separate subnets can let a host roam outside the scanned IP range entirely.
155
+ - **A second network path bypasses it.** A device that also has Ethernet (mainly laptops, not phones) can switch to it and land on an untouched segment.
156
+ - **Managed switches with Dynamic ARP Inspection can block the spoofing outright.** Irrelevant on typical home routers, matters on corporate/enterprise networks.
157
+
158
+ ## Fork-specific changes
159
+
160
+ Everything below was added in this fork, on top of upstream's last release (v1.5.0):
161
+
162
+ - IPv6 (NDP) spoofing, so `limit`/`block` also cover a host's IPv6 traffic instead of only IPv4
163
+ - Optional config file (`~/.config/evillimiter/config.ini`) for default flags and watch settings
164
+ - `-l`/`--log-file` to persist ok/error messages to a file
165
+ - `--version` flag
166
+ - mDNS/NetBIOS/DHCP hostname fallback when reverse DNS fails
167
+ - `block` now also blocks the `INPUT`/`OUTPUT` chains (traffic to/from this machine itself), not just `FORWARD`
168
+ - `watch` now shows each watched host's Online/Offline status and auto-watches hosts on `limit`/`block`
169
+ - Host tracking keyed off MAC address instead of IP (fixes reconnect detection and a hash/equality bug)
170
+ - `limit`/`block` report `tc`/`iptables` failures instead of silently claiming success
171
+ - Fixed a false-positive/duplicate-command bug in restriction teardown for combined upload+download limits/blocks
172
+ - Fixed `ByteValue` formatting for totals in the terabyte range
173
+ - CI running the test suite on every push/PR
174
+
175
+ See [CHANGELOG](CHANGELOG) for the full version history, including upstream's.
176
+
177
+ ## Contributing
178
+
179
+ Want to report a bug, request a feature, or submit a pull request? See [CONTRIBUTING.md](CONTRIBUTING.md) for the rules on filing issues and opening PRs. Please also read the [Code of Conduct](CODE_OF_CONDUCT.md).
180
+
181
+ ## Disclaimer
182
+ [Evil Limiter](https://github.com/DavidsonRafaelK/evillimiter) - originally created by [bitbrute](https://github.com/bitbrute), now maintained by [DavidsonRafaelK](https://github.com/DavidsonRafaelK) - is provided "as is" and "with all faults". Neither the original author nor the current maintainer makes any representations or warranties of any kind concerning the safety, suitability, lack of viruses, inaccuracies, typographical errors, or other harmful components of this software. There are inherent dangers in the use of any software, and you are solely responsible for determining whether Evil Limiter is compatible with your equipment and other software installed on your equipment. You are also solely responsible for the protection of your equipment and backup of your data, and neither party will be liable for any damages you may suffer in connection with using, modifying, or distributing this software.
183
+
184
+ ## License
185
+
186
+ Copyright (c) 2019-2026 by [bitbrute](https://github.com/bitbrute), copyright (c) 2026 by [DavidsonRafaelK](https://github.com/DavidsonRafaelK) for fork-specific changes. Some rights reserved.<br>
187
+ [Evil Limiter](https://github.com/DavidsonRafaelK/evillimiter) is licensed under the MIT License as stated in the [LICENSE file](LICENSE).
@@ -0,0 +1,146 @@
1
+ <p align="center"><img src="https://i.imgur.com/CBGh0Yx.png" /></p>
2
+
3
+ # Evil Limiter
4
+
5
+ [![License Badge](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
+ [![Compatibility](https://img.shields.io/badge/python-3-brightgreen.svg)](PROJECT)
7
+ [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://GitHub.com/Naereen/StrapDown.js/graphs/commit-activity)
8
+ [![Open Source Love](https://badges.frapsoft.com/os/v3/open-source.svg?v=102)](https://github.com/ellerbrock/open-source-badge/)
9
+
10
+ > This project is a maintained fork of [bitbrute/evillimiter](https://github.com/bitbrute/evillimiter). The upstream repository is no longer maintained, so this fork continues development - bug fixes, new features, and compatibility updates. See [Fork-specific changes](#fork-specific-changes) below.
11
+
12
+ A tool to monitor, analyze and limit the bandwidth (upload/download) of devices on your local network without physical or administrative access.<br>
13
+ ```evillimiter``` employs [ARP spoofing](https://en.wikipedia.org/wiki/ARP_spoofing) and [traffic shaping](https://en.wikipedia.org/wiki/Traffic_shaping) to throttle the bandwidth of hosts on the network. On IPv6-enabled networks, [NDP spoofing](https://en.wikipedia.org/wiki/Address_Resolution_Protocol#Vulnerabilities) is used alongside ARP spoofing to also cut off a host's IPv6 traffic, which would otherwise bypass the IPv4-only limit/block.
14
+
15
+ **Searching for a Windows-compatible version?**<br>
16
+ Check out the open-source alternative [EvilLimiter for Windows](https://github.com/bitbrute/evillimiter-windows).
17
+
18
+ ## Requirements
19
+ - Linux distribution
20
+ - Python 3 or greater
21
+
22
+ Possibly missing python packages will be installed during the installation process.
23
+
24
+ #### Compatibility notes
25
+
26
+ ```evillimiter``` locates ```tc```, ```iptables``` and ```sysctl``` via ```PATH``` at startup and errors clearly if one is missing. It shells out to the ```iptables``` binary specifically - on distros where ```iptables``` is an ```iptables-nft``` compatibility shim this generally works, but a firewall managed purely through native ```nft``` rules with no ```iptables``` shim installed is not something this tool talks to.
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ git clone https://github.com/DavidsonRafaelK/evillimiter.git
32
+ cd evillimiter
33
+ python3 -m venv .venv
34
+ source .venv/bin/activate
35
+ pip install .
36
+ ```
37
+
38
+ Run it with ```sudo``` (raw sockets, ```iptables```/```tc``` need root) - it's the run that needs root, not the install:
39
+
40
+ ```bash
41
+ sudo .venv/bin/evillimiter
42
+ ```
43
+
44
+ Note: ```sudo python3 setup.py install``` (the old instructions) is deprecated in modern setuptools and often fails outright with ```ModuleNotFoundError: No module named 'setuptools'``` - ```sudo``` runs the system Python, which usually doesn't have setuptools installed, unlike the venv's own Python that ```pip``` manages for you. On distros that enforce [PEP 668](https://peps.python.org/pep-0668/) (Arch, Debian 12+, ...), installing outside a venv is blocked entirely for this reason.
45
+
46
+ Alternatively, you can download a desired version from the [Release page](https://github.com/DavidsonRafaelK/evillimiter/releases).<br>
47
+
48
+ ## Usage
49
+
50
+ Type ```evillimiter``` or ```python3 bin/evillimiter``` to run the tool.
51
+
52
+ ```evillimiter``` will try to resolve required information (network interface, netmask, gateway address, ...) on its own, automatically.
53
+
54
+ <p align="center"><img src="https://raw.githubusercontent.com/DavidsonRafaelK/evillimiter/master/docs/images/screenshot.png" alt="evillimiter running in a terminal" width="800" /></p>
55
+
56
+ #### Command-Line Arguments
57
+
58
+ | Argument | Explanation |
59
+ | -------- | ----------- |
60
+ | ```-h``` | Displays help message listing all command-line arguments |
61
+ | ```-i [Interface Name]``` | Specifies network interface (resolved if not specified)|
62
+ | ```-g [Gateway IP Address]``` | Specifies gateway IP address (resolved if not specified)|
63
+ | ```-m [Gateway MAC Address]``` | Specifies gateway MAC address (resolved if not specified)|
64
+ | ```-n [Netmask Address]``` | Specifies netmask (resolved if not specified)|
65
+ | ```-f``` | Flushes current iptables and tc configuration. Ensures that packets are dealt with correctly.|
66
+ | ```--colorless``` | Disables colored output |
67
+ | ```-l [File Path]```, ```--log-file [File Path]``` | Also appends every ok/error message to this file (plain text, no color codes). |
68
+ | ```--version``` | Prints the installed version and exits. |
69
+
70
+ #### Config file
71
+
72
+ Any of the flags above (except ```-f```/```--flush```, which is a one-shot action, not a persisted preference) can be given a default in an optional ini file at ```~/.config/evillimiter/config.ini``` (or ```$XDG_CONFIG_HOME/evillimiter/config.ini```). A command-line flag always overrides the config file.
73
+
74
+ ```ini
75
+ [general]
76
+ interface = wlan0
77
+ colorless = true
78
+ log_file = /var/log/evillimiter.log
79
+
80
+ [watch]
81
+ interval = 30
82
+ range = 192.168.1.1-192.168.1.50
83
+ ```
84
+
85
+ ```[watch]``` sets the initial values normally set at runtime via ```watch set interval```/```watch set range``` (see below), so they don't need to be re-entered every session.
86
+
87
+ #### ```evillimiter``` Commands
88
+
89
+ | Command | Explanation |
90
+ | ------- | ----------- |
91
+ | ```scan (--range [IP Range])``` | Scans your network for online hosts. One of the first things to do after start.<br>```--range``` lets you specify a custom IP range.<br>For example: ```scan --range 192.168.178.1-192.168.178.40``` or just ```scan``` to scan the entire subnet.
92
+ | ```hosts (--force)``` | Displays all the hosts/devices previously scanned and basic information. Shows ID for each host that is required for interaction.<br>```--force``` forces the table to be shown, even when it doesn't fit the terminal.
93
+ | ```limit [ID1,ID2,...] [Rate] (--upload) (--download)``` | Limits bandwidth of host(s) associated to specified ID. Rate determines the internet speed. Host(s) are automatically added to the watchlist.<br>```--upload``` limits outgoing traffic only.<br>```--download``` limits incoming traffic only.<br>Valid rates: ```bit```, ```kbit```, ```mbit```, ```gbit```<br>For example: ```limit 4,5,6 200kbit``` or ```limit all 1gbit```
94
+ | ```block [ID1,ID2,...] (--upload) (--download)``` | Blocks internet connection of host(s) associated to specified ID. Host(s) are automatically added to the watchlist.<br>```--upload``` limits outgoing traffic only <br>```--download``` limits incoming traffic only.
95
+ | ```free [ID1,ID2,...]``` | Unlimits/Unblocks host(s) associated to specified ID. Removes all further restrictions.
96
+ | ```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```
97
+ | ```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```
98
+ | ```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```
99
+ | ```watch``` | Shows current watch status, including each watched host's Online/Offline state as of the last scan sweep. The watch feature detects when a host reconnects with a different IP address.<br>Hosts are added to the watchlist automatically upon ```limit``` or ```block```.
100
+ | ```watch add [ID1,ID2,...]``` | Adds specified host(s) to the watchlist.<br>For example: ```watch add 6,7,8```
101
+ | ```watch remove [ID1,ID2,...]``` | Removes specified host(s) from the watchlist.<br>For example: ```watch remove all```
102
+ | ```watch set [Attribute] [Value]``` | Changes current watch settings. The following attributes can be changed:<br>```range``` is the IP range to scan for reconnects.<br>```interval``` is the time to wait between each network scan (in seconds).<br>For example: ```watch set interval 120```
103
+ | ```clear``` | Clears the terminal window.
104
+ | ```quit``` | Quits the application.
105
+ | ```?```, ```help``` | Displays command information similar to this one.
106
+
107
+ ## Restrictions
108
+
109
+ - **Rate-limits IPv4 traffic only**, since [ARP spoofing](https://en.wikipedia.org/wiki/ARP_spoofing) requires the ARP packet that is only present on IPv4 networks. On networks with an IPv6 default route, a host's IPv6 traffic is instead fully blocked via NDP spoofing rather than rate-limited, since the ```tc```/```iptables``` rules that shape traffic are IPv4-only.
110
+ - **Cellular fallback defeats it.** A blocked/limited phone can just switch to LTE/5G once WiFi degrades (Android "avoid poor connections", iOS equivalents) - that traffic never touches your network at all.
111
+ - **MAC-randomizing devices reappear as a new host on reconnect.** Many phones present a different MAC per network join by default, so ```watch``` (matches by MAC) can't follow them; the old restriction is left bound to a MAC/IP nobody uses anymore.
112
+ - **Already-open connections can straggle.** A stream/download in progress doesn't always re-resolve the gateway's address mid-flight, so it can keep flowing on a stale ARP/NDP cache entry until it naturally resets - new connections are caught immediately.
113
+ - **Devices on a different subnet/VLAN are invisible.** Band-steering mesh systems that split 2.4GHz/5GHz onto separate subnets can let a host roam outside the scanned IP range entirely.
114
+ - **A second network path bypasses it.** A device that also has Ethernet (mainly laptops, not phones) can switch to it and land on an untouched segment.
115
+ - **Managed switches with Dynamic ARP Inspection can block the spoofing outright.** Irrelevant on typical home routers, matters on corporate/enterprise networks.
116
+
117
+ ## Fork-specific changes
118
+
119
+ Everything below was added in this fork, on top of upstream's last release (v1.5.0):
120
+
121
+ - IPv6 (NDP) spoofing, so `limit`/`block` also cover a host's IPv6 traffic instead of only IPv4
122
+ - Optional config file (`~/.config/evillimiter/config.ini`) for default flags and watch settings
123
+ - `-l`/`--log-file` to persist ok/error messages to a file
124
+ - `--version` flag
125
+ - mDNS/NetBIOS/DHCP hostname fallback when reverse DNS fails
126
+ - `block` now also blocks the `INPUT`/`OUTPUT` chains (traffic to/from this machine itself), not just `FORWARD`
127
+ - `watch` now shows each watched host's Online/Offline status and auto-watches hosts on `limit`/`block`
128
+ - Host tracking keyed off MAC address instead of IP (fixes reconnect detection and a hash/equality bug)
129
+ - `limit`/`block` report `tc`/`iptables` failures instead of silently claiming success
130
+ - Fixed a false-positive/duplicate-command bug in restriction teardown for combined upload+download limits/blocks
131
+ - Fixed `ByteValue` formatting for totals in the terabyte range
132
+ - CI running the test suite on every push/PR
133
+
134
+ See [CHANGELOG](CHANGELOG) for the full version history, including upstream's.
135
+
136
+ ## Contributing
137
+
138
+ Want to report a bug, request a feature, or submit a pull request? See [CONTRIBUTING.md](CONTRIBUTING.md) for the rules on filing issues and opening PRs. Please also read the [Code of Conduct](CODE_OF_CONDUCT.md).
139
+
140
+ ## Disclaimer
141
+ [Evil Limiter](https://github.com/DavidsonRafaelK/evillimiter) - originally created by [bitbrute](https://github.com/bitbrute), now maintained by [DavidsonRafaelK](https://github.com/DavidsonRafaelK) - is provided "as is" and "with all faults". Neither the original author nor the current maintainer makes any representations or warranties of any kind concerning the safety, suitability, lack of viruses, inaccuracies, typographical errors, or other harmful components of this software. There are inherent dangers in the use of any software, and you are solely responsible for determining whether Evil Limiter is compatible with your equipment and other software installed on your equipment. You are also solely responsible for the protection of your equipment and backup of your data, and neither party will be liable for any damages you may suffer in connection with using, modifying, or distributing this software.
142
+
143
+ ## License
144
+
145
+ Copyright (c) 2019-2026 by [bitbrute](https://github.com/bitbrute), copyright (c) 2026 by [DavidsonRafaelK](https://github.com/DavidsonRafaelK) for fork-specific changes. Some rights reserved.<br>
146
+ [Evil Limiter](https://github.com/DavidsonRafaelK/evillimiter) is licensed under the MIT License as stated in the [LICENSE file](LICENSE).
@@ -0,0 +1,17 @@
1
+ import logging
2
+ import warnings
3
+
4
+ # scapy imports Blowfish and CAST5, which the cryptography package now
5
+ # deprecates; drop those warnings before scapy is imported downstream.
6
+ warnings.filterwarnings('ignore', message=r'.*(Blowfish|CAST5).*')
7
+
8
+ # Silence scapy's runtime warning emitted on every spoofed is-at ARP reply
9
+ # (op=2 sent at layer 3 without an explicit Ethernet destination MAC).
10
+ logging.getLogger('scapy.runtime').setLevel(logging.ERROR)
11
+
12
+ # scapy warns that 'iface' has no effect on layer-3 send()/sr1() calls; the
13
+ # routing table already selects the correct interface, so drop the noise.
14
+ warnings.filterwarnings('ignore', message=r".*'iface' has no effect on L3 I/O.*")
15
+
16
+ __version__ = '1.6.0'
17
+ __description__ = 'Monitors, analyzes and limits the bandwidth of devices on the local network'
File without changes
@@ -0,0 +1,49 @@
1
+ import os
2
+ import configparser
3
+
4
+
5
+ def _default_paths():
6
+ base = os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config'))
7
+ return [os.path.join(base, 'evillimiter', 'config.ini')]
8
+
9
+
10
+ def load_config(paths=None):
11
+ """
12
+ Reads optional user defaults from an ini file (the first of `paths`
13
+ that exists; default ~/.config/evillimiter/config.ini, or the
14
+ $XDG_CONFIG_HOME equivalent). Returns a dict of only the keys
15
+ actually present - a missing or unreadable file returns {}, so
16
+ every existing hardcoded default is unaffected unless a value is
17
+ actually there.
18
+
19
+ [general]
20
+ interface, gateway_ip, gateway_mac, netmask, log_file - strings
21
+ colorless - bool
22
+ [watch]
23
+ interval - int (seconds)
24
+ range - string, same syntax as `watch set range` / -i CLI ranges
25
+ """
26
+ paths = _default_paths() if paths is None else paths
27
+ parser = configparser.ConfigParser()
28
+
29
+ if not parser.read(paths):
30
+ return {}
31
+
32
+ values = {}
33
+
34
+ if parser.has_section('general'):
35
+ general = parser['general']
36
+ for key in ('interface', 'gateway_ip', 'gateway_mac', 'netmask', 'log_file'):
37
+ if key in general:
38
+ values[key] = general[key]
39
+ if 'colorless' in general:
40
+ values['colorless'] = general.getboolean('colorless')
41
+
42
+ if parser.has_section('watch'):
43
+ watch = parser['watch']
44
+ if 'interval' in watch:
45
+ values['watch_interval'] = watch.getint('interval')
46
+ if 'range' in watch:
47
+ values['watch_range'] = watch['range']
48
+
49
+ return values
@@ -0,0 +1,9 @@
1
+ import evillimiter.console.shell as shell
2
+
3
+ BROADCAST = 'ff:ff:ff:ff:ff:ff'
4
+
5
+ BIN_TC = shell.locate_bin('tc')
6
+ BIN_IPTABLES = shell.locate_bin('iptables')
7
+ BIN_SYSCTL = shell.locate_bin('sysctl')
8
+
9
+ IP_FORWARD_LOC = 'net.ipv4.ip_forward'
File without changes
@@ -0,0 +1,18 @@
1
+ from .io import IO
2
+
3
+
4
+ _MAIN_BANNER = r"""{}
5
+ ███████╗██╗ ██╗██╗██╗ ██╗ ██╗███╗ ███╗██╗████████╗███████╗██████╗
6
+ ██╔════╝██║ ██║██║██║ ██║ ██║████╗ ████║██║╚══██╔══╝██╔════╝██╔══██╗
7
+ █████╗ ██║ ██║██║██║ ██║ ██║██╔████╔██║██║ ██║ █████╗ ██████╔╝
8
+ ██╔══╝ ╚██╗ ██╔╝██║██║ ██║ ██║██║╚██╔╝██║██║ ██║ ██╔══╝ ██╔══██╗
9
+ ███████╗ ╚████╔╝ ██║███████╗ ███████╗██║██║ ╚═╝ ██║██║ ██║ ███████╗██║ ██║
10
+ ╚══════╝ ╚═══╝ ╚═╝╚══════╝ ╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
11
+ {}by bitbrute ~ limit devices on your network :3
12
+ v[_V_]
13
+
14
+ """.format(IO.Fore.LIGHTRED_EX, IO.Style.RESET_ALL + IO.Style.BRIGHT)
15
+
16
+
17
+ def get_main_banner(version):
18
+ return _MAIN_BANNER.replace('[_V_]', version)
@@ -0,0 +1,38 @@
1
+ from evillimiter.console.io import IO
2
+
3
+
4
+ class BarChart(object):
5
+ def __init__(self, draw_char='▇', max_bar_length=30):
6
+ self.draw_char = draw_char
7
+ self.max_bar_length = max_bar_length
8
+
9
+ self._data = []
10
+
11
+ def add_value(self, value, prefix, suffix=''):
12
+ self._data.append({ 'value': value, 'prefix': prefix, 'suffix': suffix })
13
+
14
+ def get(self, reverse=False):
15
+ def remap(n, old_min, old_max, new_min, new_max):
16
+ return (((n - old_min) * (new_max - new_min)) / (old_max - old_min)) + new_min
17
+
18
+ self._data.sort(reverse=reverse, key=lambda x: x['value'])
19
+
20
+ max_value = self._data[0]['value'] if reverse else self._data[-1]['value']
21
+ max_prefix_length = max([len(x['prefix']) for x in self._data]) + 1
22
+
23
+ chart = ''
24
+
25
+ for value in self._data:
26
+ if max_value == 0:
27
+ bar_length = 0
28
+ else:
29
+ bar_length = round(remap(value['value'], 0, max_value, 0, self.max_bar_length))
30
+
31
+ chart += '{}{}: {} {}\n'.format(
32
+ value['prefix'],
33
+ ' ' * (max_prefix_length - len(value['prefix'])),
34
+ self.draw_char * bar_length,
35
+ value['suffix']
36
+ )
37
+
38
+ return chart[:-1]
@@ -0,0 +1,103 @@
1
+ import re
2
+ import logging
3
+ import colorama
4
+
5
+ from . import shell
6
+
7
+
8
+ class IO(object):
9
+ _ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?')
10
+
11
+ Back = colorama.Back
12
+ Fore = colorama.Fore
13
+ Style = colorama.Style
14
+
15
+ colorless = False
16
+ _logger = None
17
+
18
+ @staticmethod
19
+ def initialize(colorless=False, log_file=None):
20
+ """
21
+ Initializes console input and output. If log_file is given,
22
+ every ok()/error() message is additionally appended there
23
+ (plain text, ANSI stripped) - opt-in, no file is touched
24
+ otherwise.
25
+ """
26
+ IO.colorless = colorless
27
+ if not colorless:
28
+ colorama.init(autoreset=True)
29
+
30
+ if log_file:
31
+ try:
32
+ handler = logging.FileHandler(log_file)
33
+ except OSError as e:
34
+ IO.error('could not open log file {}: {}.'.format(log_file, e))
35
+ return
36
+
37
+ handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
38
+ logger = logging.getLogger('evillimiter')
39
+ logger.setLevel(logging.INFO)
40
+ logger.addHandler(handler)
41
+ IO._logger = logger
42
+
43
+ @staticmethod
44
+ def print(text, end='\n', flush=False):
45
+ """
46
+ Writes a given string to the console.
47
+ """
48
+ if IO.colorless:
49
+ text = IO._remove_colors(text)
50
+
51
+ print(text, end=end, flush=flush)
52
+
53
+ @staticmethod
54
+ def ok(text, end='\n'):
55
+ """
56
+ Print a success status message
57
+ """
58
+ IO.print('{}OK{} {}'.format(IO.Style.BRIGHT + IO.Fore.LIGHTGREEN_EX, IO.Style.RESET_ALL, text), end=end)
59
+ if IO._logger:
60
+ IO._logger.info(IO._remove_colors(text))
61
+
62
+ @staticmethod
63
+ def error(text):
64
+ """
65
+ Print an error status message
66
+ """
67
+ IO.print('{}ERR{} {}'.format(IO.Style.BRIGHT + IO.Fore.LIGHTRED_EX, IO.Style.RESET_ALL, text))
68
+ if IO._logger:
69
+ IO._logger.error(IO._remove_colors(text))
70
+
71
+ @staticmethod
72
+ def spacer():
73
+ """
74
+ Prints a blank line for attraction purposes
75
+ """
76
+ IO.print('')
77
+
78
+ @staticmethod
79
+ def input(prompt):
80
+ """
81
+ Prompts the user for input.
82
+ """
83
+ if IO.colorless:
84
+ prompt = IO._remove_colors(prompt)
85
+
86
+ return input(prompt)
87
+
88
+ @staticmethod
89
+ def clear():
90
+ """
91
+ Clears the terminal screen
92
+ """
93
+ shell.execute('clear')
94
+
95
+ @staticmethod
96
+ def _remove_colors(text):
97
+ edited = text
98
+
99
+ for match in IO._ANSI_CSI_RE.finditer(text):
100
+ s, e = match.span()
101
+ edited = edited.replace(text[s:e], '')
102
+
103
+ return edited
@@ -0,0 +1,28 @@
1
+ import os
2
+ import subprocess
3
+ from evillimiter.console.io import IO
4
+
5
+ DEVNULL = open(os.devnull, 'w')
6
+
7
+
8
+ def execute(command, root=True):
9
+ return subprocess.call('sudo ' + command if root else command, shell=True)
10
+
11
+
12
+ def execute_suppressed(command, root=True):
13
+ return subprocess.call('sudo ' + command if root else command, shell=True, stdout=DEVNULL, stderr=DEVNULL)
14
+
15
+
16
+ def output(command, root=True):
17
+ return subprocess.check_output('sudo ' + command if root else command, shell=True).decode('utf-8')
18
+
19
+
20
+ def output_suppressed(command, root=True):
21
+ return subprocess.check_output('sudo ' + command if root else command, shell=True, stderr=DEVNULL).decode('utf-8')
22
+
23
+
24
+ def locate_bin(name):
25
+ try:
26
+ return output_suppressed('which {}'.format(name)).replace('\n', '')
27
+ except subprocess.CalledProcessError:
28
+ IO.error('missing util: {}, check your PATH'.format(name))