Linux nftables Inter-VLAN Firewall: Build Stateful Firewall Rules Between VLANs
Inter-VLAN routing solves one problem: it allows hosts in different VLANs to communicate.
But in a production network, simply making every VLAN reachable from every other VLAN is rarely the desired end state.
Consider a network containing:
VLAN 10 — Users
VLAN 20 — Servers
VLAN 30 — Management
VLAN 40 — IoT
Routing can connect all four networks.
Security policy should decide which connections are actually permitted.
For example:
Users → Web Servers ALLOW HTTP/HTTPS
Users → Management DENY
IoT → Servers DENY by default
Management → Servers ALLOW SSH
Servers → DNS ALLOW DNS
This is where nftables becomes a critical part of Linux network architecture.
In this practical guide, we will transform the Linux router built in the Linux Inter-VLAN Routing with Network Namespaces guide into a stateful inter-VLAN firewall.
Instead of simply asking:
Can Linux route this packet?
we will also ask:
Should Linux allow this connection?
The final architecture will combine:
VLAN segmentation
↓
Linux routing
↓
nftables
↓
Connection tracking
↓
Stateful firewall policy
Routing and Firewalling Solve Different Problems
One of the most important concepts in Linux networking is that routing and filtering are separate decisions.
Routing answers:
Where should this packet go?
The firewall answers:
Should this packet be allowed to go there?
NAT answers another question:
Should an address or port be translated?
Therefore:
Routing ≠ Firewalling ≠ NAT
This distinction matters when troubleshooting.
Suppose:
192.168.10.10
needs to communicate with:
192.168.20.20
Linux may correctly determine:
192.168.20.0/24
↓
VLAN 20
but nftables may still intentionally drop the packet.
That is not a routing failure.
It is a security policy.
If you need to review the routing layer first, use the Linux Inter-VLAN Routing with Network Namespaces guide before implementing the firewall.
Our Inter-VLAN Firewall Architecture
We will expand the previous virtual router into four logical security zones.
Linux Router
+ nftables
|
+-------------------+-------------------+
| | |
VLAN 10 VLAN 20 VLAN 30
USERS SERVERS MANAGEMENT
192.168.10.0/24 192.168.20.0/24 192.168.30.0/24
Later we will add:
VLAN 40
IoT
192.168.40.0/24
The security model will be:
USERS
|
| HTTP/HTTPS
v
SERVERS
MANAGEMENT
|
| SSH
v
SERVERS
IoT
|
X
SERVERS
USERS
|
X
MANAGEMENT
Anything we have not explicitly allowed will be denied.
That is our core policy.
Why Use a Default-Deny Policy?
There are two broad firewall philosophies.
The first is:
Allow everything
↓
Block specific unwanted traffic
The second is:
Block everything
↓
Allow required traffic
For controlled inter-VLAN segmentation, the second model is usually much easier to reason about.
We therefore want:
policy drop
on our forwarding chain.
Traffic must match an explicit accept rule or an appropriate stateful rule before it can cross the router.
This creates:
DEFAULT
↓
DROP
EXCEPT
↓
explicitly permitted traffic
How nftables Sees Routed Traffic
nftables integrates with the Linux Netfilter framework.
For routed traffic, the simplified packet path is:
Network Interface
↓
PREROUTING
↓
Routing Decision
↓
FORWARD
↓
POSTROUTING
↓
Outgoing Interface
The official Netfilter documentation identifies forward as the hook used for packets that enter the machine but are destined for another system.
That makes the forwarding chain the natural place to enforce our inter-VLAN security policy.
Before Creating Firewall Rules
Confirm that routing works before introducing filtering.
Inside the router namespace from our previous lab:
sudo ip netns exec ns-rtr ip -br addr
Check routes:
sudo ip netns exec ns-rtr ip route
Check forwarding:
sudo ip netns exec ns-rtr \
sysctl net.ipv4.ip_forward
Expected:
net.ipv4.ip_forward = 1
If routing does not work before the firewall is installed, do not expect firewall rules to fix it.
The correct order is:
Layer 2
↓
VLAN
↓
Layer 3
↓
Routing
↓
Firewall
For VLAN troubleshooting, see Linux Bridge VLAN Filtering Explained.
Inspect Existing nftables Rules First
Before modifying a Linux firewall, always inspect the current ruleset.
Run:
sudo ip netns exec ns-rtr \
nft list ruleset
On a production machine, this step is mandatory.
Never blindly run:
nft flush ruleset
on a remote or production server.
Doing so can remove:
SSH access rules
NAT rules
VPN rules
container rules
security policies
and potentially disconnect you from the machine.
Our examples assume an isolated lab router namespace.
Create the Firewall Table
We’ll use the inet family.
Create:
sudo ip netns exec ns-rtr \
nft add table inet vlan_firewall
The inet family is useful because it can accommodate IPv4 and IPv6 rules in the same logical table when the rules are designed appropriately.
Verify:
sudo ip netns exec ns-rtr \
nft list tables
You should see:
table inet vlan_firewall
Create the Forward Chain
Now create a base chain:
sudo ip netns exec ns-rtr \
nft 'add chain inet vlan_firewall forward { type filter hook forward priority 0; policy drop; }'
The critical part is:
hook forward
because we are filtering routed traffic.
And:
policy drop
means unmatched forwarded packets are discarded.
Our router has now moved from:
Route everything
to:
Route only traffic permitted by firewall policy
Verify the Chain
Run:
sudo ip netns exec ns-rtr \
nft list table inet vlan_firewall
You should see something conceptually similar to:
table inet vlan_firewall {
chain forward {
type filter hook forward priority filter;
policy drop;
}
}
At this point, inter-VLAN communication may stop.
That is expected.
We created a firewall whose default behavior is:
DROP
but have not added any exceptions yet.
Understanding Stateful Firewalling
A stateless firewall might inspect every packet independently.
A stateful firewall understands that packets belong to connections.
For example:
Client
|
| TCP SYN
v
Server
followed by:
Client
^
| SYN-ACK
|
Server
and later:
Client ←→ Server
These packets belong to the same connection.
Linux Netfilter tracks connection information using conntrack.
Important connection states include:
NEW
ESTABLISHED
RELATED
INVALID
This allows us to express policies such as:
Allow VLAN 10 to initiate HTTPS to VLAN 20
without writing a separate broad rule that allows VLAN 20 to initiate arbitrary connections back toward VLAN 10.
What Does ct state established Mean?
Suppose VLAN 10 starts:
192.168.10.10
↓
TCP/443
↓
192.168.20.20
The initial connection is new.
After connection tracking has seen the appropriate traffic, packets can be associated with an established connection.
So instead of creating a broad reverse rule:
VLAN 20 → VLAN 10
ALLOW EVERYTHING
we can permit:
ESTABLISHED
RELATED
traffic.
This is one of the central benefits of a stateful firewall.
Allow Established and Related Traffic
Add:
sudo ip netns exec ns-rtr \
nft add rule inet vlan_firewall forward \
ct state established,related accept
Now inspect:
sudo ip netns exec ns-rtr \
nft list chain inet vlan_firewall forward
The conceptual policy becomes:
Existing permitted connection?
|
YES
|
v
ACCEPT
Otherwise
|
v
Continue evaluating rules
The Netfilter nftables documentation defines established and related as connection-tracking states that can be matched directly by nftables rules.
Drop Invalid Traffic Early
We can also explicitly handle invalid connection-tracking state:
sudo ip netns exec ns-rtr \
nft add rule inet vlan_firewall forward \
ct state invalid drop
Our early chain becomes:
INVALID
↓
DROP
ESTABLISHED / RELATED
↓
ACCEPT
Then new connections must match explicit security policy.
Build the User-to-Server Policy
Assume:
VLAN 10
192.168.10.0/24
contains users.
And:
VLAN 20
192.168.20.0/24
contains application servers.
Users need:
HTTP → TCP/80
HTTPS → TCP/443
but they should not receive unrestricted access to server services.
Add:
sudo ip netns exec ns-rtr \
nft add rule inet vlan_firewall forward \
ip saddr 192.168.10.0/24 \
ip daddr 192.168.20.0/24 \
tcp dport { 80, 443 } \
ct state new accept
The policy is now:
VLAN 10
|
+------ TCP/80 ------+
| |
+------ TCP/443 -----+----> VLAN 20
while something like:
VLAN 10
|
TCP/22
|
X
|
VLAN 20
does not match the rule and eventually reaches the default-drop policy.
Why Specify Source and Destination Networks?
You could create an overly broad rule such as:
tcp dport 443 accept
but that may allow HTTPS traffic between zones you never intended to connect.
A stronger security rule expresses context:
SOURCE
+
DESTINATION
+
PROTOCOL
+
PORT
+
STATE
For example:
192.168.10.0/24
↓
192.168.20.0/24
↓
TCP
↓
80 / 443
↓
NEW
↓
ACCEPT
This makes the firewall policy easier to audit.
Create a Management VLAN
Now imagine:
VLAN 30
192.168.30.0/24
is reserved for administrators.
The management network needs SSH access to servers.
Policy:
Management
|
| SSH
v
Servers
Add:
sudo ip netns exec ns-rtr \
nft add rule inet vlan_firewall forward \
ip saddr 192.168.30.0/24 \
ip daddr 192.168.20.0/24 \
tcp dport 22 \
ct state new accept
Now:
VLAN 30 → VLAN 20 → SSH
is permitted.
But:
VLAN 10 → VLAN 20 → SSH
remains blocked unless another rule allows it.
This is real network segmentation.
Restrict SSH to Specific Administration Hosts
We can go further.
Instead of allowing the entire management subnet:
192.168.30.0/24
suppose only:
192.168.30.10
is the administration workstation.
Then use:
sudo ip netns exec ns-rtr \
nft add rule inet vlan_firewall forward \
ip saddr 192.168.30.10 \
ip daddr 192.168.20.0/24 \
tcp dport 22 \
ct state new accept
The security boundary becomes much narrower:
Admin Workstation
192.168.30.10
|
SSH
|
v
Server VLAN
rather than:
Anything in Management VLAN
↓
SSH
↓
Servers
Restrict SSH to One Server
Suppose only:
192.168.20.10
should accept administrative SSH.
Use:
sudo ip netns exec ns-rtr \
nft add rule inet vlan_firewall forward \
ip saddr 192.168.30.10 \
ip daddr 192.168.20.10 \
tcp dport 22 \
ct state new accept
Now the rule describes:
WHO
192.168.30.10
CAN REACH
192.168.20.10
USING
TCP/22
This is substantially easier to reason about during a security audit.
Add an IoT VLAN
IoT networks are a useful example because devices often need restricted outbound connectivity but should not have broad access to trusted infrastructure.
Suppose:
VLAN 40
192.168.40.0/24
contains cameras, sensors, and smart devices.
We do not create a general rule:
192.168.40.0/24
↓
192.168.20.0/24
↓
ACCEPT
Therefore, under our default-drop model:
IoT
|
X
|
Servers
is already blocked.
This demonstrates an important property of default-deny design:
You do not need a drop rule for every unwanted relationship.
Unmatched traffic is denied automatically.
Allow DNS to a Dedicated Resolver
Suppose the network has an internal DNS resolver:
192.168.20.53
and VLAN 10 needs DNS.
Allow UDP DNS:
sudo ip netns exec ns-rtr \
nft add rule inet vlan_firewall forward \
ip saddr 192.168.10.0/24 \
ip daddr 192.168.20.53 \
udp dport 53 \
ct state new accept
DNS can also use TCP, so if your design requires normal TCP DNS operation, add:
sudo ip netns exec ns-rtr \
nft add rule inet vlan_firewall forward \
ip saddr 192.168.10.0/24 \
ip daddr 192.168.20.53 \
tcp dport 53 \
ct state new accept
Now users can reach:
192.168.20.53:53
without gaining broad access to the entire server VLAN.
Allow Multiple Services with Sets
One of nftables’ strengths is its support for sets.
Instead of separate rules:
TCP/80
TCP/443
we used:
tcp dport { 80, 443 }
You can extend this concept.
For example:
{ 22, 80, 443 }
But do not combine ports simply because nftables allows it.
Security policy should remain understandable.
A rule representing:
Web services
should not quietly include:
SSH
database
monitoring
unless those services genuinely share the same trust relationship.
Rule Order Matters
Suppose your chain contains:
1. established,related accept
2. VLAN10 → Web Server HTTPS accept
3. VLAN30 → Server SSH accept
4. default policy drop
Packets are evaluated according to the ruleset.
Design rule order intentionally.
A useful conceptual structure is:
Invalid traffic
↓
Established traffic
↓
Highly specific trusted rules
↓
Application/service rules
↓
Logging
↓
Default drop
This also makes the ruleset easier for another administrator to understand.
Add Counters
During troubleshooting, it is useful to know whether a rule is actually being matched.
For example:
sudo ip netns exec ns-rtr \
nft add rule inet vlan_firewall forward \
ip saddr 192.168.10.0/24 \
ip daddr 192.168.20.0/24 \
tcp dport 443 \
counter \
ct state new accept
After generating traffic:
sudo ip netns exec ns-rtr \
nft list chain inet vlan_firewall forward
you can inspect packet and byte counters.
This helps answer:
Did traffic reach this rule?
rather than assuming the firewall is responsible.
Add Logging Carefully
You can log selected traffic before dropping it.
For example:
sudo ip netns exec ns-rtr \
nft add rule inet vlan_firewall forward \
ip saddr 192.168.40.0/24 \
ip daddr 192.168.20.0/24 \
limit rate 5/second \
log prefix \"IOT_BLOCKED: \"
Because the chain policy is already drop, traffic can then fall through to the default policy.
Why rate-limit logging?
Because a busy or malicious host could otherwise generate enormous numbers of log messages.
Never turn a firewall into a log-based denial-of-service against your own server.
Test Allowed HTTPS Traffic
Assume a web server is listening at:
192.168.20.20:443
From VLAN 10:
curl -k https://192.168.20.20/
If the HTTPS policy is correct:
VLAN 10
|
TCP/443
|
v
VLAN 20
should succeed.
Then inspect:
sudo ip netns exec ns-rtr \
nft list chain inet vlan_firewall forward
The relevant counter should increase if one was configured.
Test Blocked SSH Traffic
From VLAN 10:
ssh 192.168.20.20
Under our policy:
USERS
|
SSH
|
X
|
SERVERS
should be blocked.
But from the authorized management workstation:
192.168.30.10
SSH should be permitted if the server and routing configuration are correct.
This gives us a useful validation matrix:
| Source | Destination | Service | Expected |
|---|---|---|---|
| Users | Web Server | HTTPS | Allow |
| Users | Server | SSH | Block |
| Management | Server | SSH | Allow |
| IoT | Server | SSH | Block |
| Users | DNS Server | DNS | Allow |
Do not test only the traffic you expect to work.
A security policy must also be tested for traffic that must fail.
Why Return Traffic Works
Suppose the user VLAN starts an HTTPS session:
VLAN 10
|
NEW HTTPS
|
v
VLAN 20
The initial connection matches our explicit HTTPS rule.
Return traffic can then match:
ct state established,related accept
Therefore we do not need:
VLAN 20
↓
allow all traffic
↓
VLAN 10
This is the foundation of stateful firewalling.
The nftables conntrack system recognizes states including new, established, related, invalid, and untracked.
NEW vs ESTABLISHED vs RELATED
A simplified mental model is:
NEW
|
| connection begins
v
ESTABLISHED
|
| connection continues
v
ESTABLISHED
RELATED is used for traffic associated with an existing connection but represented as a separate connection.
Do not confuse:
RELATED
with:
any packet from a host we previously trusted
It has a specific connection-tracking meaning.
Inspect Connection Tracking
If the conntrack utility is installed, you can inspect tracked connections:
sudo ip netns exec ns-rtr \
conntrack -L
Generate an HTTPS connection and inspect again.
You can then correlate:
Application traffic
↓
Conntrack entry
↓
nftables state rule
This is an excellent way to understand why return traffic is accepted.
Firewalling Does Not Replace VLANs
A tempting design is:
One giant LAN
+
Firewall rules
But VLANs and firewalls solve different parts of segmentation.
VLANs provide Layer 2 separation.
Routing creates controlled Layer 3 paths.
nftables applies security policy to those paths.
Together:
VLAN
↓
Layer 2 boundary
↓
Router
↓
Layer 3 boundary
↓
Firewall
↓
Security policy
For the Layer 2 component, see Linux Bridge VLAN Filtering Explained.
Firewalling Does Not Replace Routing
Another mistake is to troubleshoot every failed connection by editing firewall rules.
Suppose:
192.168.10.10
has no route to its gateway.
Adding:
accept
to nftables will not fix that.
Likewise, if Linux chooses the wrong interface, the underlying issue may be route selection.
CorgiNetwork’s Linux Traffic Uses the Wrong Interface covers that diagnostic path.
Firewalling and Policy Routing
Now consider a more advanced router:
VLAN 10 → ISP A
VLAN 20 → ISP B
The firewall may allow the packet correctly, while policy routing determines which WAN connection it uses.
The path becomes:
Packet
↓
Firewall / Netfilter processing
↓
Routing policy
↓
Routing table
↓
WAN interface
If source-dependent routing is required, use Linux Policy Routing with ip rule and Multiple Routing Tables.
For persistent Ubuntu configurations involving several gateways, see Netplan Multiple Default Routes and Policy Routing.
Firewalling and Asymmetric Routing
Stateful firewalls care about connection state.
That makes asymmetric network paths particularly important.
Imagine:
Request
VLAN 10
↓
Router A
↓
Server
Reply
Server
↓
Router B
↓
VLAN 10
Depending on the architecture, one firewall may not observe both directions as expected.
Before blaming conntrack, inspect:
forward path
return path
routing tables
policy rules
CorgiNetwork’s Linux rp_filter and Asymmetric Routing explains another Linux mechanism that can affect asymmetric designs.
Troubleshooting: Everything Is Blocked
You enable:
policy drop
and suddenly nothing works.
First inspect:
sudo ip netns exec ns-rtr \
nft list ruleset
Then verify whether you have:
ct state established,related accept
and explicit rules for new permitted connections.
Check counters.
Then capture traffic:
sudo ip netns exec ns-rtr \
tcpdump -eni any
If the packet never reaches the router, nftables is not the first problem.
Troubleshooting: HTTPS Is Blocked
Check the routing decision:
sudo ip netns exec ns-rtr \
ip route get 192.168.20.20
Then verify:
sudo ip netns exec ns-rtr \
nft list chain inet vlan_firewall forward
Confirm:
source network
destination network
protocol
destination port
all match the traffic you are generating.
Then capture:
sudo ip netns exec ns-rtr \
tcpdump -eni any host 192.168.20.20
Do not assume a port number without verifying what the application actually uses.
Troubleshooting: Request Arrives but Reply Fails
This is a classic problem.
Suppose:
Client → Server
appears in the packet capture, but:
Server → Client
does not return.
Inspect the server’s route:
ip route
The server needs a valid return path.
Then inspect conntrack:
conntrack -L
and firewall counters.
A firewall cannot compensate for a fundamentally incorrect return route.
Troubleshooting: Rule Counter Stays at Zero
If you added:
counter
and the value remains zero, the traffic is not matching that rule.
Possible causes include:
wrong source subnet
wrong destination subnet
wrong protocol
wrong port
earlier rule already handled packet
traffic never reached router
wrong chain/hook
This is valuable evidence.
A zero counter can be more useful than repeatedly rewriting the rule.
Troubleshooting: VLAN Traffic Never Reaches nftables
Return to Layer 2.
Check:
bridge vlan show
Then:
bridge fdb show
Inspect interfaces:
ip -br link
Capture the VLAN/trunk:
tcpdump -eni INTERFACE vlan
If VLAN tagging or bridge membership is wrong, nftables cannot fix it.
Read Linux Bridge Networking with Network Namespaces and veth if the bridge path itself is unclear.
A Professional Troubleshooting Workflow
For a failed connection:
Client
↓
Firewall Router
↓
Server
check the following order:
1. Client interface
↓
2. Client IP address
↓
3. Client route
↓
4. Gateway ARP
↓
5. VLAN membership
↓
6. Router ingress
↓
7. Routing decision
↓
8. nftables forward rule
↓
9. Router egress
↓
10. Server receives packet
↓
11. Server return route
↓
12. Conntrack state
↓
13. Return firewall processing
↓
14. Client receives reply
This is dramatically more effective than changing firewall rules until something starts working.
Use nft monitor trace for Difficult Problems
For advanced nftables troubleshooting, packet tracing can show how packets move through the ruleset.
A typical workflow involves enabling nftables tracing for carefully selected traffic and then using:
nft monitor trace
This can reveal:
which chain was entered
which rule matched
which verdict was returned
Because tracing can generate substantial output, target it narrowly in production environments.
Build Security Zones, Not Random Rules
As the network grows, think in terms of zones:
USERS
SERVERS
MANAGEMENT
IoT
GUEST
DMZ
BACKUP
MONITORING
Then document relationships:
USERS → SERVERS
HTTP/HTTPS
MANAGEMENT → SERVERS
SSH/HTTPS
SERVERS → BACKUP
Backup protocol
IoT → INTERNAL
DENY
GUEST → INTERNAL
DENY
This scales better than accumulating hundreds of unrelated firewall commands.
Example Production Policy Matrix
A more realistic architecture might use:
| Source Zone | Destination | Service | Policy |
|---|---|---|---|
| Users | Web Servers | 80/443 | Allow |
| Users | Database | 5432 | Deny |
| Users | Management | Any | Deny |
| App Servers | Database | 5432 | Allow |
| Management | Servers | 22/443 | Allow |
| IoT | Internal Servers | Any | Deny |
| Guest | Internal Networks | Any | Deny |
The firewall configuration should be derived from this policy—not the other way around.
Application-to-Database Segmentation
Suppose:
VLAN 20
Application Servers
192.168.20.0/24
and:
VLAN 50
Database
192.168.50.0/24
Only PostgreSQL should be permitted:
nft add rule inet vlan_firewall forward \
ip saddr 192.168.20.0/24 \
ip daddr 192.168.50.0/24 \
tcp dport 5432 \
ct state new accept
Now:
Application
|
PostgreSQL
|
v
Database
is allowed while unrelated services remain blocked by the default policy.
This is significantly stronger than merely placing the database in another VLAN while permitting unrestricted routing to it.
Why Internal Segmentation Matters
A firewall should not exist only at the Internet edge.
Consider an attacker who compromises a user workstation.
Without internal controls:
Compromised PC
↓
Servers
↓
Database
↓
Management
↓
Backups
may all be reachable.
With segmentation:
Compromised PC
↓
User VLAN
↓
Firewall
↓
Only explicitly allowed services
the network places additional constraints on lateral movement.
VLANs create the topology.
The firewall enforces the trust relationships.
nftables and Linux Network Namespaces
Our lab uses a router namespace because network namespaces isolate networking resources.
This lets us maintain a separate:
routing table
interfaces
neighbor table
firewall
for the virtual router.
The architecture started in Linux Network Namespaces Explained and now evolves into:
Network Namespace
↓
Virtual Router
↓
VLAN Interfaces
↓
Routing
↓
nftables Firewall
This is an excellent platform for learning enterprise networking without requiring multiple physical routers and switches.
Persist the Ruleset
Interactive commands are ideal for a lab.
Production firewalls should be stored in a persistent configuration and loaded through the operating system’s supported nftables service.
A typical ruleset structure might resemble:
table inet vlan_firewall {
chain forward {
type filter hook forward priority filter;
policy drop;
ct state invalid drop
ct state established,related accept
# Explicit zone policies follow
}
}
The exact persistence mechanism depends on the Linux distribution.
Always validate the ruleset before applying major firewall changes remotely.
Production Safety
Before changing a remote firewall:
✓ Back up the existing ruleset
✓ Keep an active management session
✓ Verify out-of-band access if available
✓ Validate syntax
✓ Avoid flushing production rules blindly
✓ Test new policy incrementally
✓ Confirm SSH management rules first
✓ Monitor counters
✓ Capture packets when behavior is unclear
A technically correct firewall policy is still dangerous if deployed without a recovery path.
Do Not Forget IPv6
A common security mistake is designing excellent IPv4 segmentation while ignoring IPv6.
If IPv6 is enabled and routed in your environment, the security model must account for it as well.
Using the inet nftables family can help organize IPv4 and IPv6 filtering under one table, but address-family-specific expressions and policies still need to be designed correctly.
Do not assume:
IPv4 blocked
automatically means:
all network paths blocked
Frequently Asked Questions
What is an nftables inter-VLAN firewall?
It is a Linux firewall configuration that filters routed traffic between different VLAN networks using nftables.
For example:
VLAN 10
↓
nftables
↓
VLAN 20
The router provides reachability while nftables determines which connections are permitted.
Should inter-VLAN traffic be allowed by default?
That depends on the network’s requirements, but security-focused segmentation commonly benefits from a default-deny model in which required communication is explicitly permitted.
The important point is to derive the policy from actual application and administration requirements.
What is the nftables forward chain?
A base chain attached to the forward hook can inspect packets that are being routed through the Linux system rather than delivered to a local process.
That makes it central to Linux router and firewall configurations.
What does ct state established,related do?
It matches packets associated with established connections and related connections tracked by Netfilter conntrack.
This allows return traffic for permitted stateful connections without creating broad reverse-direction access rules.
Do I need NAT between VLANs?
Usually not when the router has direct routes to both internal networks and each network has a valid return path.
NAT and routing solve different problems.
Should I use iptables or nftables?
Modern Linux systems commonly use nftables as the current Netfilter packet-classification interface. When maintaining an existing environment, however, first determine which firewall framework and compatibility layers the distribution and applications are already using before replacing anything.
How can I see my nftables rules?
Use:
nft list ruleset
For our namespace lab:
sudo ip netns exec ns-rtr \
nft list ruleset
How do I troubleshoot an nftables rule that is not matching?
Use:
nft list ruleset
counters
tcpdump
conntrack
ip route get
nft monitor trace
and troubleshoot from the source toward the destination rather than changing multiple layers simultaneously.
Conclusion
Building VLANs is only the beginning of network segmentation.
A complete architecture looks like:
Endpoint
↓
VLAN
↓
Linux Bridge
↓
Router
↓
nftables
↓
Security Policy
↓
Destination VLAN
The VLAN answers:
Which Layer 2 domain does this system belong to?
The router answers:
Where should the packet go?
nftables answers:
Should this connection be permitted?
And conntrack gives the firewall the state required to distinguish new connections from traffic belonging to established or related flows.
The essential design pattern is:
Default Drop
+
Established/Related
+
Explicit New Connections
=
Controlled Inter-VLAN Communication
The most useful diagnostic commands are:
nft list ruleset
ip route
ip route get DESTINATION
ip neigh
bridge vlan show
bridge fdb show
conntrack -L
tcpdump -eni INTERFACE
nft monitor trace
But the commands are less important than the troubleshooting order.
First prove that Layer 2 works.
Then prove that routing works.
Then inspect the firewall.
Then verify the return path.
This article extends the CorgiNetwork Linux networking path established in Linux Network Namespaces Explained, Linux Bridge Networking with Network Namespaces and veth, Linux Bridge VLAN Filtering Explained, and Linux Inter-VLAN Routing with Network Namespaces.
For more complex route selection, Linux Policy Routing with ip rule and Multiple Routing Tables explains how Linux can select different routing tables based on policy.
For persistent multi-gateway Ubuntu environments, Netplan Multiple Default Routes and Policy Routing extends that architecture.
When packets unexpectedly leave through the wrong NIC, Linux Traffic Uses the Wrong Interface provides a packet-path troubleshooting workflow.
For multi-interface ARP behavior, Linux ARP Flux and Multiple NICs on the Same Subnet addresses neighbor-resolution problems.
And when asymmetric routing interacts with Linux reverse-path validation, Linux rp_filter and Asymmetric Routing explains how to diagnose the issue correctly.
Together, the cluster now forms:
Network Namespaces
↓
veth
↓
Linux Bridge
↓
VLAN Filtering
↓
Inter-VLAN Routing
↓
nftables Firewall
↓
Stateful Security
↓
Policy Routing
↓
Advanced Linux Network Architecture