From 836f4557f2e6c2c12b3ee096f590d0a4e9ef9067 Mon Sep 17 00:00:00 2001 From: Matthew Grotke Date: Tue, 9 Jun 2026 13:52:07 -0400 Subject: [PATCH] Development --- PRO_FEATURES.md | 15 +++++++++++++++ .../app/pages/networklayout/content.json | 2 +- .../routlin-dash/app/pages/overview/content.json | 2 +- docker/routlin-dash/app/pages/overview/view.py | 6 +++--- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/PRO_FEATURES.md b/PRO_FEATURES.md index 4c8cb27..d6d5051 100644 --- a/PRO_FEATURES.md +++ b/PRO_FEATURES.md @@ -111,6 +111,21 @@ In all modes, captive portal behavior is a per-VLAN flag, consistent with Routli --- +## Feature 10: Usage Rate Monitoring and Automatic Suspension + +Routlin Pro will track inbound and outbound traffic rates per external IP address and automatically suspend connections that exceed configurable thresholds. + +- **Per-IP rate monitoring** - real-time tracking of bandwidth consumption by external source/destination IP +- **Configurable rate limits** - set thresholds by bytes per second, requests per second, or connection count within a rolling time window +- **Automatic suspension** - IPs exceeding limits are temporarily blocked via nftables; the block duration is configurable (e.g. 5 minutes, 1 hour, permanent until manually cleared) +- **Dashboard visibility** - live view of top external IPs by usage, with current rate, total bytes transferred, and suspension status +- **Allowlist** - trusted IPs (CDNs, VPN endpoints, remote offices) can be exempted from rate enforcement +- **Alerting** - optional notifications when an IP is suspended, including the triggering rule and measured rate + +Designed to protect against bandwidth abuse, accidental runaway processes, and low-level denial-of-service from external sources without requiring a full IPS deployment. + +--- + ## Feature 9: Mobile-Aware Dashboard Layout Routlin Pro will include a responsive, mobile-optimized layout for the entire dashboard, allowing administrators to monitor and manage their network from a phone or tablet. diff --git a/docker/routlin-dash/app/pages/networklayout/content.json b/docker/routlin-dash/app/pages/networklayout/content.json index aaae79f..8707f4e 100644 --- a/docker/routlin-dash/app/pages/networklayout/content.json +++ b/docker/routlin-dash/app/pages/networklayout/content.json @@ -84,7 +84,7 @@ } }, { - "label": "Record", + "label": "Recorded", "field": "dnsmasq_log_queries", "class": "col-narrow", "render": "badge_yes_no", diff --git a/docker/routlin-dash/app/pages/overview/content.json b/docker/routlin-dash/app/pages/overview/content.json index afd0140..83e070b 100644 --- a/docker/routlin-dash/app/pages/overview/content.json +++ b/docker/routlin-dash/app/pages/overview/content.json @@ -57,7 +57,7 @@ "type": "stat_card", "label": "Queries Blocked", "value": "%STAT_BLOCKED_TODAY%", - "sub": "since midnight", + "sub": "in last 24h", "variant": "warning" }, { diff --git a/docker/routlin-dash/app/pages/overview/view.py b/docker/routlin-dash/app/pages/overview/view.py index d692282..e0bcad1 100644 --- a/docker/routlin-dash/app/pages/overview/view.py +++ b/docker/routlin-dash/app/pages/overview/view.py @@ -8,7 +8,7 @@ from pages.dhcpleases.view import live_dhcp_leases def get_dnsmasq_stats(): stats = {'queries': '-', 'hits': '-', 'hit_rate': '-', 'forwarded': '-', 'auth': '-', 'tcp_peak': '-'} - out = factory.run('journalctl -u dnsmasq -n 200 --no-pager 2>/dev/null') + out = factory.run("journalctl -u 'dnsmasq-routlin-*' -n 200 --no-pager 2>/dev/null") for line in reversed(out.splitlines()): if 'queries forwarded' in line: m = re.search(r'queries forwarded (\d+)', line) @@ -36,8 +36,8 @@ def get_dnsmasq_stats(): def count_blocked_today(): - out = factory.run("journalctl -u dnsmasq --since today --no-pager 2>/dev/null | grep -c 'is NXDOMAIN'") - return out or '0' + out = factory.run("journalctl -u 'dnsmasq-routlin-*' --since '24 hours ago' --no-pager 2>/dev/null | grep -c ' is 0\\.0\\.0\\.0'") + return out.strip() or '0' def count_blocked_domains():