From 26c93fe02c46ee463b5dd3f0007c10f337d59546 Mon Sep 17 00:00:00 2001 From: Tommaso Bailetti Date: Wed, 27 May 2026 11:57:00 +0200 Subject: [PATCH 1/5] fix(netifyd): accepting untracked traffic instead of queueing it --- packages/netifyd/files/usr/sbin/ns-netifyd-configure.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/netifyd/files/usr/sbin/ns-netifyd-configure.py b/packages/netifyd/files/usr/sbin/ns-netifyd-configure.py index 1ceb533a2..164c4a7bd 100644 --- a/packages/netifyd/files/usr/sbin/ns-netifyd-configure.py +++ b/packages/netifyd/files/usr/sbin/ns-netifyd-configure.py @@ -38,6 +38,8 @@ type filter hook input priority filter + 10; policy accept; iifname lo accept + # Skip untracked traffic + ct state untracked accept # Accept traffic matching bypass set ip saddr @nfq_bypass_v4 accept ip daddr @nfq_bypass_v4 accept @@ -55,6 +57,8 @@ chain nfq_forward { type filter hook forward priority filter + 10; policy accept; + # Skip untracked traffic + ct state untracked accept # Accept traffic matching bypass set ip saddr @nfq_bypass_v4 accept ip daddr @nfq_bypass_v4 accept @@ -73,6 +77,8 @@ type filter hook output priority filter + 10; policy accept; oifname lo accept + # Skip untracked traffic + ct state untracked accept # Accept traffic matching bypass set ip saddr @nfq_bypass_v4 accept ip daddr @nfq_bypass_v4 accept From 33173dd9e65432da8a2c1eccd0a9e53cb969ce2f Mon Sep 17 00:00:00 2001 From: Tommaso Bailetti Date: Wed, 27 May 2026 11:58:34 +0200 Subject: [PATCH 2/5] perf(netifyd): updated the buffer size 10X --- packages/netifyd/files/etc/netifyd/profiles.d/00-default.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/netifyd/files/etc/netifyd/profiles.d/00-default.conf b/packages/netifyd/files/etc/netifyd/profiles.d/00-default.conf index 687f9d690..27afecf22 100644 --- a/packages/netifyd/files/etc/netifyd/profiles.d/00-default.conf +++ b/packages/netifyd/files/etc/netifyd/profiles.d/00-default.conf @@ -157,7 +157,7 @@ all = include [netlink] # Set the Netlink buffer size -buffer_size = 32768 +buffer_size = 327680 bridge_pvid_discovery = no # vim: set ft=dosini : From 368b4d39552ce8f0dfc91f0587153b8b27b42f27 Mon Sep 17 00:00:00 2001 From: Tommaso Bailetti Date: Mon, 8 Jun 2026 11:44:33 +0200 Subject: [PATCH 3/5] feat(netifyd): added exclusion toggle for firewall traffic --- packages/netifyd/README.md | 24 +++++++++++++++++++ .../files/usr/sbin/ns-netifyd-configure.py | 21 +++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/packages/netifyd/README.md b/packages/netifyd/README.md index a751550f6..57641ab0f 100644 --- a/packages/netifyd/README.md +++ b/packages/netifyd/README.md @@ -63,3 +63,27 @@ uci del_list netifyd.config.bypassv4='192.168.1.0/24 | My network' uci commit netifyd reload_config ``` + +## Firewall-local traffic analysis + +The `firewall_traffic` option controls how traffic to/from the firewall itself is handled. It accepts three values: + +| Value | Description | +|---|---| +| `full` | Analyze all firewall traffic: inbound, outbound (firewall-initiated), and forwarded | +| `inbound` | Analyze only traffic initiated from outside toward the firewall, plus forwarded traffic (default) | +| `forward` | Analyze only forwarded traffic; ignore all firewall-local traffic entirely | + +```bash +# Default: only inbound connections to the firewall + forwarded traffic +uci set netifyd.config.firewall_traffic='inbound' + +# Full analysis including firewall-originated connections (e.g. DNS, monitoring) +uci set netifyd.config.firewall_traffic='full' + +# Forwarded traffic only, skip all local firewall traffic +uci set netifyd.config.firewall_traffic='forward' + +uci commit netifyd +reload_config +``` diff --git a/packages/netifyd/files/usr/sbin/ns-netifyd-configure.py b/packages/netifyd/files/usr/sbin/ns-netifyd-configure.py index 164c4a7bd..e15dde463 100644 --- a/packages/netifyd/files/usr/sbin/ns-netifyd-configure.py +++ b/packages/netifyd/files/usr/sbin/ns-netifyd-configure.py @@ -34,12 +34,18 @@ } # push input packets to userspace queues for DPI analysis +{%- if firewall_traffic in ('full', 'inbound') %} chain nfq_input { type filter hook input priority filter + 10; policy accept; iifname lo accept # Skip untracked traffic ct state untracked accept +{%- if firewall_traffic == 'inbound' %} + # Skip reply traffic for connections initiated by the firewall itself + # (e.g. DNS responses, monitoring ping replies) + ct direction reply accept +{%- endif %} # Accept traffic matching bypass set ip saddr @nfq_bypass_v4 accept ip daddr @nfq_bypass_v4 accept @@ -52,6 +58,7 @@ # Traffic to queues 54-57 queue flags bypass to 54-57 } +{%- endif %} # push forward packets to userspace queues for DPI analysis chain nfq_forward { @@ -72,6 +79,7 @@ queue flags bypass to 50-53 } +{%- if firewall_traffic == 'full' %} # push output packets to userspace queues for DPI analysis chain nfq_output { type filter hook output priority filter + 10; policy accept; @@ -91,6 +99,8 @@ # Traffic to queues 54-57 queue flags bypass to 54-57 } +{%- endif %} + } """ @@ -131,11 +141,20 @@ def generate_nfq_table(): # Format elements with optional comments for nftables v4_elements = _format_nft_elements(v4_raw) v6_elements = _format_nft_elements(v6_raw) - + + # Read which firewall-local traffic to include in DPI analysis: + # 'full' - analyze all firewall traffic (input + output + forward) + # 'inbound' - analyze only traffic initiated from outside toward the firewall (input + forward) + # 'forward' - analyze only forwarded traffic, ignore firewall-local traffic entirely + firewall_traffic = e_uci.get('netifyd', 'config', 'firewall_traffic', dtype=str, default='inbound') + if firewall_traffic not in ('full', 'inbound', 'forward'): + firewall_traffic = 'inbound' + template = Environment(loader=BaseLoader()).from_string(NFQ_TABLE) render = template.render( v4_elements=v4_elements, v6_elements=v6_elements, + firewall_traffic=firewall_traffic, ) # Apply nftables From 2d9c0db952fdb76a2ac07f4b46d9310276dcedc8 Mon Sep 17 00:00:00 2001 From: Tommaso Bailetti Date: Wed, 17 Jun 2026 12:02:30 +0200 Subject: [PATCH 4/5] refactor: using chain selector for all nft chains --- .../files/usr/sbin/ns-netifyd-configure.py | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/packages/netifyd/files/usr/sbin/ns-netifyd-configure.py b/packages/netifyd/files/usr/sbin/ns-netifyd-configure.py index e15dde463..e8bcf6356 100644 --- a/packages/netifyd/files/usr/sbin/ns-netifyd-configure.py +++ b/packages/netifyd/files/usr/sbin/ns-netifyd-configure.py @@ -34,14 +34,14 @@ } # push input packets to userspace queues for DPI analysis -{%- if firewall_traffic in ('full', 'inbound') %} +{%- if 'input' in firewall_chains %} chain nfq_input { type filter hook input priority filter + 10; policy accept; iifname lo accept # Skip untracked traffic ct state untracked accept -{%- if firewall_traffic == 'inbound' %} +{%- if 'output' not in firewall_chains %} # Skip reply traffic for connections initiated by the firewall itself # (e.g. DNS responses, monitoring ping replies) ct direction reply accept @@ -61,6 +61,7 @@ {%- endif %} # push forward packets to userspace queues for DPI analysis +{%- if 'forward' in firewall_chains %} chain nfq_forward { type filter hook forward priority filter + 10; policy accept; @@ -78,8 +79,9 @@ # Traffic to queues 50-53 queue flags bypass to 50-53 } +{%- endif %} -{%- if firewall_traffic == 'full' %} +{%- if 'output' in firewall_chains %} # push output packets to userspace queues for DPI analysis chain nfq_output { type filter hook output priority filter + 10; policy accept; @@ -142,19 +144,26 @@ def generate_nfq_table(): v4_elements = _format_nft_elements(v4_raw) v6_elements = _format_nft_elements(v6_raw) - # Read which firewall-local traffic to include in DPI analysis: - # 'full' - analyze all firewall traffic (input + output + forward) - # 'inbound' - analyze only traffic initiated from outside toward the firewall (input + forward) - # 'forward' - analyze only forwarded traffic, ignore firewall-local traffic entirely - firewall_traffic = e_uci.get('netifyd', 'config', 'firewall_traffic', dtype=str, default='inbound') - if firewall_traffic not in ('full', 'inbound', 'forward'): - firewall_traffic = 'inbound' + # Read which nftables chains to add for DPI analysis. + # Valid values: 'input', 'output', 'forward'. + # Defaults to all three chains if the option is missing. + valid_chains = {'input', 'output', 'forward'} + raw_chains = e_uci.get('netifyd', 'config', 'firewall_traffic', dtype=str, list=True, default=list(valid_chains)) + firewall_chains = [] + for chain in raw_chains: + if chain in valid_chains: + firewall_chains.append(chain) + else: + print(f"Warning: invalid firewall_traffic value '{chain}', ignoring. Valid values: {sorted(valid_chains)}") + if not firewall_chains: + print("Warning: no valid firewall_traffic chains configured, defaulting to all chains.") + firewall_chains = list(valid_chains) template = Environment(loader=BaseLoader()).from_string(NFQ_TABLE) render = template.render( v4_elements=v4_elements, v6_elements=v6_elements, - firewall_traffic=firewall_traffic, + firewall_chains=firewall_chains, ) # Apply nftables From 24ccf338c0f306701ee2a90517443924761ed136 Mon Sep 17 00:00:00 2001 From: Tommaso Bailetti Date: Wed, 17 Jun 2026 12:15:56 +0200 Subject: [PATCH 5/5] using stderr to print warnings --- packages/netifyd/files/usr/sbin/ns-netifyd-configure.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/netifyd/files/usr/sbin/ns-netifyd-configure.py b/packages/netifyd/files/usr/sbin/ns-netifyd-configure.py index e8bcf6356..d0e2cf052 100644 --- a/packages/netifyd/files/usr/sbin/ns-netifyd-configure.py +++ b/packages/netifyd/files/usr/sbin/ns-netifyd-configure.py @@ -7,6 +7,7 @@ import os import subprocess +import sys from euci import EUci from jinja2 import Environment, BaseLoader @@ -154,9 +155,9 @@ def generate_nfq_table(): if chain in valid_chains: firewall_chains.append(chain) else: - print(f"Warning: invalid firewall_traffic value '{chain}', ignoring. Valid values: {sorted(valid_chains)}") + print(f"Warning: invalid firewall_traffic value '{chain}', ignoring. Valid values: {sorted(valid_chains)}", file=sys.stderr) if not firewall_chains: - print("Warning: no valid firewall_traffic chains configured, defaulting to all chains.") + print("Warning: no valid firewall_traffic chains configured, defaulting to all chains.", file=sys.stderr) firewall_chains = list(valid_chains) template = Environment(loader=BaseLoader()).from_string(NFQ_TABLE) @@ -173,7 +174,7 @@ def generate_nfq_table(): subprocess.run(['nft', '-f', NFQ_TABLE_FILE], check=True, capture_output=True) os.unlink(NFQ_TABLE_FILE) except subprocess.CalledProcessError as e: - print(f"Error applying nftables configuration: {e.stderr.decode()}") + print(f"Error applying nftables configuration: {e.stderr.decode()}", file=sys.stderr) if __name__ == "__main__":