Development

This commit is contained in:
Matthew Grotke 2026-06-06 17:14:01 -04:00
parent e37029a066
commit 574a45111d
8 changed files with 164 additions and 110 deletions

View file

@ -75,94 +75,21 @@ The core Routlin software will always remain free for individual use. Pro featur
--- ---
### Pro Feature 1: Deep Packet Inspection (DPI) and Device Identification **Deep Packet Inspection and Device Identification** — See exactly what every device on your network is doing. Routlin Pro automatically classifies devices and identifies traffic types in real time — streaming, gaming, P2P, VoIP, and more — feeding into a Security Insights dashboard and per-device traffic rules.
Routlin Pro will analyze traffic at the packet level to identify: **Intrusion Detection and Prevention (IDS/IPS)** — Monitor your network for known threat signatures across all traffic, not just DNS. Choose alert-only mode or automatic blocking. Signature database updated regularly, with an optional extended commercial threat feed.
- **Device categories** - automatically classify connected devices (phones, laptops, smart TVs, IoT sensors, gaming consoles) based on traffic fingerprints **SSL/TLS Traffic Inspection** — See inside encrypted HTTPS traffic for security monitoring and content filtering. Routlin Pro decrypts, inspects, and re-encrypts on the fly, enabling IDS/IPS and anomaly detection to work on traffic that would otherwise be completely opaque.
- **Traffic categories** - identify streaming, gaming, P2P, VoIP, cloud backup, and other traffic types in real time
- **Per-device usage breakdowns** - see what each device on the network is actually doing
This data surfaces in a Security Insights dashboard and feeds into traffic rules, allowing administrators to block or rate-limit specific applications for specific devices or device categories. **Traffic Flows (Session Logging)** — A full log of every TCP and UDP connection through the router: source, destination, port, bytes, and timing. Filter, sort, and save presets. Invaluable for diagnosing bandwidth problems or investigating unexpected activity after the fact.
--- **Anomaly and Pattern Detection** — Routlin Pro watches for unusual behavior automatically: unexpected large transfers, SYN flood indicators, overnight activity on idle devices, new device types appearing, and more. Anomalies surface as dashboard alerts and can trigger automated responses like device isolation or rate limiting.
### Pro Feature 2: Intrusion Detection and Prevention (IDS/IPS) **Restricted VLANs** — Prevent devices assigned to a particular VLAN from ever contacting the internet. Perfect for IoT devices, security cameras, NAS, printers, or machines running untrusted software — basically anything that should never phone home. Works alongside Routlin's inter-VLAN exception rules so you can still grant selective access to the quarantined device(s) from within the LAN only.
Routlin Pro will monitor all network traffic for known threat signatures using DPI across multiple network layers: **Supplicant-Based 802.1X Authentication** — Go beyond authorizing devices onto your network based on their MAC addresses. Routlin Pro adds full EAP-PEAP, EAP-TTLS, and EAP-TLS support, letting devices authenticate with credentials or certificates. Revoke individual device access without changing network passwords — and keep out anyone spoofing a known MAC.
- **IDS mode** - monitors and alerts on suspicious activity without blocking **Captive Portal** — Turn any VLAN into a captive portal. Choose from a simple splash/terms-of-service page, time-limited voucher codes, or a full RADIUS login using your existing user accounts. Ideal for guest networks, venues, or any situation where you need to control and track who gets internet access.
- **IPS mode** - automatically blocks detected threats in real time
- Generates a log of alerts with details on the source, destination, and matched signature
- Signature database updated regularly; an optional extended subscription provides access to a broader commercial threat database
---
### Pro Feature 3: SSL/TLS Traffic Inspection
Routlin Pro will support intercepting and inspecting encrypted HTTPS traffic for security monitoring and content filtering:
- Performs SSL/TLS decryption, analyzes packet contents, then re-encrypts using the gateway's own certificate
- Configurable by traffic category or specific domains - inspect everything or only targeted categories
- Supports a high number of concurrent sessions suitable for home and small office environments
- Requires the gateway certificate to be installed on client devices for transparent operation
This enables security features (IDS/IPS, anomaly detection) to operate on traffic that would otherwise be opaque.
---
### Pro Feature 4: Traffic Flows (Session Logging)
Routlin Pro will provide detailed logs of every network session passing through the router:
- Full connection records including source IP, destination IP, protocol, port, bytes transferred, and session timing
- Not limited to DNS queries - captures all TCP/UDP flows
- Filterable and sortable views; save custom filter presets for repeated analysis
- Useful for diagnosing bandwidth issues, identifying unexpected connections, and post-incident investigation
---
### Pro Feature 5: Anomaly and Pattern Detection
Building on DPI and session logging, Routlin Pro will surface unusual network patterns automatically:
- Large or unexpected outbound data transfers
- TCP SYN flood indicators
- Unexpected VPN or tunneling activity
- P2P and torrent detection
- High usage outside configured hours (e.g. overnight activity on a device that should be idle)
- New device types appearing on the network
Anomalies generate alerts in the dashboard and can optionally trigger automated responses such as device isolation or rate limiting.
---
### Pro Feature 6: Restricted VLANs
Routlin Pro will allow any VLAN to be designated as "restricted" - blocking all internet access for devices on that VLAN while still allowing local communication.
Use cases include:
- **IoT and smart home devices** - devices that need to talk to each other locally but should never reach the internet
- **Security cameras** - local NVR access only, no cloud uploads
- **Guest networks** - complete WAN isolation
- **Kids' devices** - internet access blocked, local resources still reachable
- **Security and privacy** - isolate local LLMs or prevent untrusted software from dialing out
Restricted VLANs work in combination with Routlin's existing inter-VLAN exception rules, so a restricted device can still be granted access to a specific device or subnet on another VLAN (e.g. a NAS or a print server) without opening internet access.
---
### Pro Feature 7: Supplicant-Based 802.1X Authentication
Routlin currently supports MAC Authentication Bypass (MAB), where the switch or AP sends a device's MAC address to RADIUS passively - the device itself does nothing. This is easy to deploy but MAC addresses can be spoofed.
Routlin Pro will add full supplicant-based 802.1X, where the client device actively participates in authentication using:
- **EAP-PEAP / EAP-TTLS** - username and password credentials, common for corporate WiFi where employees authenticate with domain credentials
- **EAP-TLS** - client certificates installed on each device, the most secure option
- Certificate management for issuing and revoking client credentials
This allows individual device certificates to be revoked without changing network passwords, and prevents unauthorized devices from gaining access even if they spoof a known MAC address.
--- ---

110
PRO_FEATURES.md Normal file
View file

@ -0,0 +1,110 @@
# Routlin Pro - Feature Specifications
Routlin Pro is a paid license tier. The core Routlin software remains free for individual use. Pro features are advanced capabilities that require ongoing maintenance, threat database subscriptions, and significant development investment to build and sustain.
Early Kickstarter backers receive a Routlin Pro license as an investment incentive.
---
## Feature 1: Deep Packet Inspection (DPI) and Device Identification
Routlin Pro will analyze traffic at the packet level to identify:
- **Device categories** - automatically classify connected devices (phones, laptops, smart TVs, IoT sensors, gaming consoles) based on traffic fingerprints
- **Traffic categories** - identify streaming, gaming, P2P, VoIP, cloud backup, and other traffic types in real time
- **Per-device usage breakdowns** - see what each device on the network is actually doing
This data surfaces in a Security Insights dashboard and feeds into traffic rules, allowing administrators to block or rate-limit specific applications for specific devices or device categories.
---
## Feature 2: Intrusion Detection and Prevention (IDS/IPS)
Routlin Pro will monitor all network traffic for known threat signatures using DPI across multiple network layers:
- **IDS mode** - monitors and alerts on suspicious activity without blocking
- **IPS mode** - automatically blocks detected threats in real time
- Generates a log of alerts with details on the source, destination, and matched signature
- Signature database updated regularly; an optional extended subscription provides access to a broader commercial threat database
---
## Feature 3: SSL/TLS Traffic Inspection
Routlin Pro will support intercepting and inspecting encrypted HTTPS traffic for security monitoring and content filtering:
- Performs SSL/TLS decryption, analyzes packet contents, then re-encrypts using the gateway's own certificate
- Configurable by traffic category or specific domains - inspect everything or only targeted categories
- Supports a high number of concurrent sessions suitable for home and small office environments
- Requires the gateway certificate to be installed on client devices for transparent operation
This enables security features (IDS/IPS, anomaly detection) to operate on traffic that would otherwise be opaque.
---
## Feature 4: Traffic Flows (Session Logging)
Routlin Pro will provide detailed logs of every network session passing through the router:
- Full connection records including source IP, destination IP, protocol, port, bytes transferred, and session timing
- Not limited to DNS queries - captures all TCP/UDP flows
- Filterable and sortable views; save custom filter presets for repeated analysis
- Useful for diagnosing bandwidth issues, identifying unexpected connections, and post-incident investigation
---
## Feature 5: Anomaly and Pattern Detection
Building on DPI and session logging, Routlin Pro will surface unusual network patterns automatically:
- Large or unexpected outbound data transfers
- TCP SYN flood indicators
- Unexpected VPN or tunneling activity
- P2P and torrent detection
- High usage outside configured hours (e.g. overnight activity on a device that should be idle)
- New device types appearing on the network
Anomalies generate alerts in the dashboard and can optionally trigger automated responses such as device isolation or rate limiting.
---
## Feature 6: Restricted VLANs
Routlin Pro allows any VLAN to be designated as "restricted" - blocking all internet access for devices on that VLAN while still allowing local communication.
Use cases include:
- **IoT and smart home devices** - devices that need to talk to each other locally but should never reach the internet
- **Security cameras** - local NVR access only, no cloud uploads
- **Guest networks** - complete WAN isolation
- **Kids' devices** - internet access blocked, local resources still reachable
- **Security and privacy** - isolate local LLMs or prevent untrusted software from dialing out
Restricted VLANs work in combination with Routlin's existing inter-VLAN exception rules, so a restricted device can still be granted access to a specific device or subnet on another VLAN (e.g. a NAS or a print server) without opening internet access.
---
## Feature 7: Supplicant-Based 802.1X Authentication
Routlin currently supports MAC Authentication Bypass (MAB), where the switch or AP sends a device's MAC address to RADIUS passively - the device itself does nothing. This is easy to deploy but MAC addresses can be spoofed.
Routlin Pro adds full supplicant-based 802.1X, where the client device actively participates in authentication using:
- **EAP-PEAP / EAP-TTLS** - username and password credentials, common for corporate WiFi where employees authenticate with domain credentials
- **EAP-TLS** - client certificates installed on each device, the most secure option
- Certificate management for issuing and revoking client credentials
This allows individual device certificates to be revoked without changing network passwords, and prevents unauthorized devices from gaining access even if they spoof a known MAC address.
---
## Feature 8: Captive Portal
Routlin Pro will support designating any VLAN as a captive portal network, intercepting unauthenticated clients and requiring them to pass through a configurable gateway page before gaining internet access.
Three portal modes:
- **Splash / Terms of Service** - guests click to accept terms before internet access is granted. No credentials required. Suitable for compliance-minded guest networks.
- **Voucher-based access** - the administrator generates single-use or time-limited voucher codes. Guests enter the code on the portal page. Supports configurable session duration, bandwidth limits, and concurrent device limits per voucher. Dashboard UI for generating, viewing, and revoking voucher batches.
- **RADIUS-authenticated login** - the portal collects a username and password and validates against Routlin's built-in FreeRADIUS server, using the same user table as 802.1X. No separate credential store needed.
In all modes, captive portal behavior is a per-VLAN flag, consistent with Routlin's existing restricted VLAN and RADIUS default VLAN model. Unauthenticated clients on the VLAN are redirected to the portal via nftables; authenticated sessions are tracked and expire based on configurable timeouts.

View file

@ -58,7 +58,7 @@ def _restricted_vlan_subnets():
vlans = load_config().get('vlans', []) vlans = load_config().get('vlans', [])
result = [] result = []
for v in vlans: for v in vlans:
if v.get('restricted_vlan') and v.get('subnet') and v.get('subnet_mask') is not None: if v.get('restricted_vlan') in ('q', 'c') and v.get('subnet') and v.get('subnet_mask') is not None:
result.append(f"{v['subnet']}/{v['subnet_mask']}") result.append(f"{v['subnet']}/{v['subnet_mask']}")
return result return result
@ -945,6 +945,15 @@ def build_table_cell(value, render_fn, col_class='', field='', row_idx=None,
inner = f'<span class="badge badge-disabled"{tip}>No</span>' inner = f'<span class="badge badge-disabled"{tip}>No</span>'
return f'{td_open}{inner}</td>' return f'{td_open}{inner}</td>'
if render_fn == 'badge_vlan_restriction':
if value == 'q':
inner = '<span class="badge badge-danger" data-tooltip="Quarantined VLAN">Q</span>'
elif value == 'c':
inner = '<span class="badge badge-warning" data-tooltip="Captive Portal VLAN">C</span>'
else:
inner = '<span class="badge badge-disabled">No</span>'
return f'{td_open}{inner}</td>'
if render_fn == 'badge_recording_on_off': if render_fn == 'badge_recording_on_off':
if str(value).lower() in ('true', '1', 'yes'): if str(value).lower() in ('true', '1', 'yes'):
inner = '<span class="badge badge-enabled">Recording On</span>' inner = '<span class="badge badge-enabled">Recording On</span>'

View file

@ -49,14 +49,15 @@ def vlans_addedit():
radius_default = 'radius_default' in request.form radius_default = 'radius_default' in request.form
mdns_reflection = 'mdns_reflection' in request.form mdns_reflection = 'mdns_reflection' in request.form
dnsmasq_log_queries = 'dnsmasq_log_queries' in request.form dnsmasq_log_queries = 'dnsmasq_log_queries' in request.form
restricted_vlan = 'restricted_vlan' in request.form restricted_vlan_raw = request.form.get('restricted_vlan', '').strip()
restricted_vlan = restricted_vlan_raw if restricted_vlan_raw in ('q', 'c') else ''
use_blocklists = sanitize.filterlist( use_blocklists = sanitize.filterlist(
request.form.getlist('use_blocklists'), request.form.getlist('use_blocklists'),
{b.get('name') for b in load_config().get('dns_blocking', {}).get('blocklists', [])}, {b.get('name') for b in load_config().get('dns_blocking', {}).get('blocklists', [])},
) )
if restricted_vlan and not PRO_LICENSE: if restricted_vlan and not PRO_LICENSE:
flash('Restricted VLAN requires a Routlin Pro license.', 'error') flash('Quarantined and Captive Portal VLANs require a Routlin Pro license.', 'error')
return redirect(f'/{_PAGE}') return redirect(f'/{_PAGE}')
if not name: if not name:
@ -269,8 +270,7 @@ def vlans_addedit():
'use_blocklists': use_blocklists, 'use_blocklists': use_blocklists,
'server_identities': new_identities, 'server_identities': new_identities,
}) })
if PRO_LICENSE: existing['restricted_vlan'] = restricted_vlan if PRO_LICENSE else ''
existing['restricted_vlan'] = restricted_vlan
if dhcp_info: if dhcp_info:
existing['dhcp_information'] = dhcp_info existing['dhcp_information'] = dhcp_info
else: else:
@ -330,8 +330,7 @@ def vlans_addedit():
'mdns_reflection': mdns_reflection, 'mdns_reflection': mdns_reflection,
'server_identities': new_identities, 'server_identities': new_identities,
} }
if PRO_LICENSE: entry['restricted_vlan'] = restricted_vlan if PRO_LICENSE else ''
entry['restricted_vlan'] = restricted_vlan
if dhcp_info: if dhcp_info:
entry['dhcp_information'] = dhcp_info entry['dhcp_information'] = dhcp_info
if is_vpn: if is_vpn:

View file

@ -97,11 +97,7 @@
"label": "Restricted", "label": "Restricted",
"field": "restricted_vlan", "field": "restricted_vlan",
"class": "col-narrow", "class": "col-narrow",
"render": "badge_yes_no", "render": "badge_vlan_restriction"
"render_options": {
"title_true": "Restricted VLAN",
"title_false": "Not Restricted"
}
} }
], ],
"row_actions": [ "row_actions": [
@ -350,11 +346,11 @@
}, },
{ {
"type": "field", "type": "field",
"label": "%RESTRICTED_VLAN_LABEL%", "label": "Restricted VLAN",
"name": "restricted_vlan", "name": "restricted_vlan",
"input_type": "checkbox", "input_type": "select",
"disabled": "%RESTRICTED_VLAN_DISABLED%", "options": "%RESTRICTED_VLAN_OPTIONS%",
"hint": "Block devices on this VLAN from communicating with the Internet. Block all LAN traffic as well (except where Inter-VLAN-Exception rules allow)." "hint": "Quarantined VLAN devices are blocked from communicating with the Internet and may only be reached by Inter-VLAN-Exception rules. Captive Portal VLAN devices are redirected to an authentication page until they complete a login or accept terms."
}, },
{ {
"type": "button_row", "type": "button_row",

View file

@ -13,8 +13,19 @@ def collect_tokens(cfg):
tokens['EXISTING_VLAN_IDS_JSON'] = json.dumps([v.get('vlan_id') for v in vlans]) tokens['EXISTING_VLAN_IDS_JSON'] = json.dumps([v.get('vlan_id') for v in vlans])
tokens['EXISTING_VLAN_NAMES_JSON'] = json.dumps([v.get('name') for v in vlans]) tokens['EXISTING_VLAN_NAMES_JSON'] = json.dumps([v.get('name') for v in vlans])
tokens['RADIUS_DEFAULT_VLAN'] = f'"{dv["name"]}" (VLAN {dv["vlan_id"]})' if dv else 'none set' tokens['RADIUS_DEFAULT_VLAN'] = f'"{dv["name"]}" (VLAN {dv["vlan_id"]})' if dv else 'none set'
tokens['RESTRICTED_VLAN_LABEL'] = 'Restricted VLAN' if PRO_LICENSE else 'Restricted VLAN (PRO FEATURE)' tokens['PRO_LICENSE_JS'] = 'true' if PRO_LICENSE else ''
tokens['RESTRICTED_VLAN_DISABLED'] = '' if PRO_LICENSE else 'true' if PRO_LICENSE:
tokens['RESTRICTED_VLAN_OPTIONS'] = json.dumps([
{'value': '', 'label': 'Unrestricted'},
{'value': 'q', 'label': 'Quarantined'},
{'value': 'c', 'label': 'Captive Portal'},
])
else:
tokens['RESTRICTED_VLAN_OPTIONS'] = json.dumps([
{'value': '', 'label': 'Unrestricted'},
{'value': 'q', 'label': 'Quarantined (PRO REQUIRED)', 'disabled': True},
{'value': 'c', 'label': 'Captive Portal (PRO REQUIRED)', 'disabled': True},
])
tokens['BLOCKLIST_NAME_OPTIONS'] = json.dumps([ tokens['BLOCKLIST_NAME_OPTIONS'] = json.dumps([
{'value': bl.get('name', ''), 'label': bl.get('description', bl.get('name', ''))} {'value': bl.get('name', ''), 'label': bl.get('description', bl.get('name', ''))}
for bl in cfg.get('dns_blocking', {}).get('blocklists', []) for bl in cfg.get('dns_blocking', {}).get('blocklists', [])

View file

@ -417,7 +417,7 @@ def build_nft_config(data, dry_run=False):
L.append(" # Allow each VLAN -> WAN (outbound internet)") L.append(" # Allow each VLAN -> WAN (outbound internet)")
for vlan in vlans: for vlan in vlans:
if vlan.get('restricted_vlan'): if vlan.get('restricted_vlan') in ('q', 'c'):
continue continue
L.append(f" iif \"{validation.derive_interface(vlan, data)}\" oif \"{wan}\" accept # {vlan['name']} -> WAN") L.append(f" iif \"{validation.derive_interface(vlan, data)}\" oif \"{wan}\" accept # {vlan['name']} -> WAN")
L.append("") L.append("")
@ -425,20 +425,22 @@ def build_nft_config(data, dry_run=False):
if container_bridges: if container_bridges:
L.append(" # Allow VLAN -> Docker bridge forwarding") L.append(" # Allow VLAN -> Docker bridge forwarding")
for vlan in vlans: for vlan in vlans:
if vlan.get('restricted_vlan'): if vlan.get('restricted_vlan') in ('q', 'c'):
continue continue
for bridge in container_bridges: for bridge in container_bridges:
L.append(f" iif \"{validation.derive_interface(vlan, data)}\" oif \"{bridge}\" ct state new accept" L.append(f" iif \"{validation.derive_interface(vlan, data)}\" oif \"{bridge}\" ct state new accept"
f" # {vlan['name']} -> {bridge}") f" # {vlan['name']} -> {bridge}")
L.append("") L.append("")
restricted = [v for v in vlans if v.get('restricted_vlan')] quarantined = [v for v in vlans if v.get('restricted_vlan') == 'q']
if restricted: if quarantined:
L.append(" # Block restricted VLANs -> WAN") L.append(" # Block quarantined VLANs -> WAN")
for vlan in restricted: for vlan in quarantined:
L.append(f" iif \"{validation.derive_interface(vlan, data)}\" oif \"{wan}\" drop # {vlan['name']} -> WAN (restricted)") L.append(f" iif \"{validation.derive_interface(vlan, data)}\" oif \"{wan}\" drop # {vlan['name']} -> WAN (quarantined)")
L.append("") L.append("")
# TODO: captive portal VLANs ('c') - PREROUTING REDIRECT rules for HTTP/HTTPS + dynamic allow-set
L += [ L += [
" # Allow Docker containers -> WAN (outbound internet access)", " # Allow Docker containers -> WAN (outbound internet access)",
f" iif != \"{wan}\" oif \"{wan}\" ct state new accept", f" iif != \"{wan}\" oif \"{wan}\" ct state new accept",

View file

@ -850,7 +850,7 @@ def validate_config(data):
try: try:
nat_addr = ipaddress.IPv4Address(nat_ip_str) nat_addr = ipaddress.IPv4Address(nat_ip_str)
for v in _all_vlans: for v in _all_vlans:
if not v.get("restricted_vlan"): if v.get("restricted_vlan") not in ('q', 'c'):
continue continue
try: try:
vnet = ipaddress.IPv4Network(f"{v['subnet']}/{v['subnet_mask']}", strict=False) vnet = ipaddress.IPv4Network(f"{v['subnet']}/{v['subnet_mask']}", strict=False)
@ -860,7 +860,7 @@ def validate_config(data):
errors.append( errors.append(
f"Port forwarding rule '{desc}' is enabled but its destination " f"Port forwarding rule '{desc}' is enabled but its destination "
f"({nat_ip_str}) is on restricted VLAN '{v['name']}'. " f"({nat_ip_str}) is on restricted VLAN '{v['name']}'. "
f"Disable the rule or remove the restricted_vlan flag." f"Disable the rule or clear the VLAN restriction setting."
) )
break break
except Exception: except Exception:
@ -959,7 +959,7 @@ def check_portfwd_restricted_vlan(nat_ip, vlans):
except Exception: except Exception:
return None return None
for v in vlans: for v in vlans:
if not v.get('restricted_vlan'): if v.get('restricted_vlan') not in ('q', 'c'):
continue continue
try: try:
net = ipaddress.IPv4Network(f"{v['subnet']}/{v['subnet_mask']}", strict=False) net = ipaddress.IPv4Network(f"{v['subnet']}/{v['subnet_mask']}", strict=False)
@ -976,7 +976,7 @@ def disable_portfwd_on_restricted_vlans(data):
Mutates data in place. Returns list of descriptions of rules that were disabled.""" Mutates data in place. Returns list of descriptions of rules that were disabled."""
restricted_nets = [] restricted_nets = []
for v in data.get('vlans', []): for v in data.get('vlans', []):
if v.get('restricted_vlan'): if v.get('restricted_vlan') in ('q', 'c'):
try: try:
restricted_nets.append(ipaddress.IPv4Network(f"{v['subnet']}/{v['subnet_mask']}", strict=False)) restricted_nets.append(ipaddress.IPv4Network(f"{v['subnet']}/{v['subnet_mask']}", strict=False))
except Exception: except Exception: