Skip to content

Commit

Permalink
Add " Interface IP" display capability + More modern CSS style (#25)
Browse files Browse the repository at this point in the history
* add display of interface ip capability - in main.py

* add display of interface ip capability - in main.html

* add improved css style - in pure-min.css

* Enhance TCGUI Interface Design

- Improved the overall layout and styling of the interface table.
- Added a cleaner and modern design using CSS.
- Ensured all interfaces are displayed and properly aligned.
- Added hover effects for buttons to enhance interactivity.
- Updated the color scheme for better readability and aesthetics.
- Maintained functionality while improving visual appeal.

* Update main.py

Co-authored-by: Markus Hofbauer <[email protected]>

* add if match else "No IP found" - main.py

Co-authored-by: Markus Hofbauer <[email protected]>

* rename css file to gui_styles.css

* remove old file

* add suggestion for stdout decode

* rolled back proc and output separation main.py

Display of IP addresses started causing issues after last edit, had to rollback to my initial contribution so UI will display IP correctly

* Replace Popen with subprocess.run

use subprocess.run instead of subprocess.Popen for
retrieving IP

* Add error handling to get_interfaces functions

* Add new GUI screenshot to the readme

* Delete tcgui.png

* run - `pre-commit autoupdate`

* add setuptools dependency for `pre-commit run -a` failure

* lines added by `pre-commit run -a`

* network troubleshooting tools added to Dockerfile for net testing

* Update .pre-commit-config.yaml

* add docker-compose documentation

* fix - Correctly retrieve IP addresses for network interfaces

* default: network_mode: host

* Remove redundancy in IP command execution

* run pre-commit autoupdate

* fixes by `pre-commit run -a`

* pre-commit autoupdate

* pyupgrade downgrade to 3.16 due to python 3.8 dependency

---------

Co-authored-by: Markus Hofbauer <[email protected]>
  • Loading branch information
benjisho and hofbi authored Aug 11, 2024
1 parent 1a5196e commit e996853
Show file tree
Hide file tree
Showing 10 changed files with 444 additions and 62 deletions.
27 changes: 14 additions & 13 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ default_stages:
- commit
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.6.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
Expand All @@ -18,14 +18,14 @@ repos:
- id: requirements-txt-fixer
- id: trailing-whitespace
- repo: https://github.com/executablebooks/mdformat
rev: 0.7.16
rev: 0.7.17
hooks:
- id: mdformat
additional_dependencies:
- mdformat-gfm
- mdformat-black
- repo: https://github.com/jumanjihouse/pre-commit-hook-yamlfmt
rev: 0.2.2
rev: 0.2.3
hooks:
- id: yamlfmt
args:
Expand All @@ -37,23 +37,23 @@ repos:
- '2'
- --implicit_start
- repo: https://github.com/psf/black
rev: 22.10.0
rev: 24.8.0
hooks:
- id: black
language_version: python3
- id: black-jupyter
language_version: python3
- repo: https://github.com/asottile/blacken-docs
rev: v1.12.1
rev: 1.18.0
hooks:
- id: blacken-docs
- repo: https://github.com/PyCQA/isort
rev: 5.10.1
rev: 5.13.2
hooks:
- id: isort
args: [--profile, black]
- repo: https://github.com/PyCQA/docformatter
rev: v1.5.0
rev: v1.7.5
hooks:
- id: docformatter
args:
Expand All @@ -63,33 +63,34 @@ repos:
- --wrap-descriptions
- '81'
- repo: https://github.com/PyCQA/flake8
rev: 6.0.0
rev: 7.1.1
hooks:
- id: flake8
additional_dependencies:
- flake8-use-pathlib
- flake8-bugbear
- flake8-pytest-style
- flake8-comprehensions
- setuptools
- repo: https://github.com/PyCQA/pylint
rev: v2.15.8
rev: v3.2.6
hooks:
- id: pylint
- repo: https://github.com/asottile/pyupgrade
rev: v3.3.1
rev: v3.16.0
hooks:
- id: pyupgrade
args:
- --py38-plus
- repo: https://github.com/asottile/yesqa
rev: v1.4.0
rev: v1.5.0
hooks:
- id: yesqa
- repo: https://github.com/PyCQA/autoflake
rev: v2.0.0
rev: v2.3.1
hooks:
- id: autoflake
- repo: https://github.com/crate-ci/typos
rev: typos-dict-v0.9.13
rev: v1.23.6
hooks:
- id: typos
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM python:3.8-alpine

RUN apk add --no-cache iproute2 && \
RUN apk add --no-cache iproute2 mtr curl tcpdump net-tools iperf3 && \
rm -rf /var/cache/apk/*

RUN pip3 install Flask
Expand Down
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

A lightweight Python-based Web-GUI for Linux traffic control (`tc`) to set, view and delete traffic shaping rules. The Web-GUI is intended for short-term isolated testbeds or classroom scenarios and does not contain any security mechanisms.

![tcgui screenshot](tcgui.png)
![tcgui screenshot](tcgui-new.png)

## Requirements

Expand Down Expand Up @@ -49,6 +49,17 @@ You can change the configuration using these Environment Variables:
- **TCGUI_DEV** - The interfaces to restrict to
- **TCGUI_REGEX** - A regex to match interfaces
### Docker-Compose
```shell
docker compose up --build

## To run in the background:
docker compose up -d --build
```
> Make sure you read the line of `network_mode` inside the `docker-compose.yml`
If using an interface bridge, docker might cause issue with the bridge. ([askubunut](https://askubuntu.com/questions/1073501/docker-breaks-network-bridging-to-virtual-machines))
To fix this, create a file `/etc/docker/daemon.json` with the following contents:
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ services:
- 5000:5000
cap_add:
- NET_ADMIN
## Uncomment if you want to control the traffic of the host machine:
network_mode: host
42 changes: 41 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,13 @@ def parse_arguments():
@app.route("/")
def main():
rules = get_active_rules()
interfaces = get_interfaces()
return render_template(
"main.html", rules=rules, units=BANDWIDTH_UNITS, standard_unit=STANDARD_UNIT
"main.html",
rules=rules,
units=BANDWIDTH_UNITS,
standard_unit=STANDARD_UNIT,
interfaces=interfaces,
)


Expand Down Expand Up @@ -140,6 +145,21 @@ def filter_interface_name(interface):
return re.sub(r"[^A-Za-z0-9_-]+", "", interface)


def run_ip_command(command_args):
"""Runs the 'ip' command with the specified arguments and returns the output."""
try:
result = subprocess.run(
command_args,
capture_output=True,
text=True,
check=True,
)
return result.stdout
except subprocess.CalledProcessError as e:
print(f"Error executing command '{command_args}': {e}")
return ""


def get_active_rules():
proc = subprocess.Popen(["tc", "qdisc"], stdout=subprocess.PIPE)
output = proc.communicate()[0].decode()
Expand All @@ -150,16 +170,36 @@ def get_active_rules():
arguments = line.split()
rule = parse_rule(arguments)
if rule["name"] and rule["name"] not in dev:
rule["ip"] = get_interface_ip(rule["name"])
rules.append(rule)
dev.add(rule["name"])
rules.sort(key=lambda x: x["name"])
return rules


def get_interfaces():
output = run_ip_command(["ip", "-o", "-4", "addr", "show"])
interfaces = {}
for line in output.split("\n"):
if line:
parts = line.split()
iface = parts[1]
ip = parts[3].split("/")[0]
interfaces[iface] = ip
return interfaces


def get_interface_ip(interface):
output = run_ip_command(["ip", "-o", "-4", "addr", "show", interface])
match = re.search(r"inet (\d+\.\d+\.\d+\.\d+)", output)
return match.group(1) if match else "No IP found"


def parse_rule(split_rule):
# pylint: disable=too-many-branches
rule = {
"name": None,
"ip": None,
"rate": None,
"delay": None,
"delayVariance": None,
Expand Down
Loading

0 comments on commit e996853

Please sign in to comment.