Firewall
Ze manages nftables packet filter and NAT rules from a single firewall { } YANG
section. The abstract data model describes matches (from) and actions (then);
the nft backend lowers them to nftables kernel expressions.
Backend
| Backend | Platform | Default | Mechanism |
|---|---|---|---|
nft |
Linux | β | google/nftables netlink library |
firewall {
backend nft;
}
Tables and Chains
Ze owns all tables whose kernel name starts with ze_. A table contains one or
more chains; a base chain has a type, hook, priority, and default policy.
firewall {
backend nft;
table wan {
family inet;
chain input {
type filter;
hook input;
priority 0;
policy drop;
term allow-ssh {
from {
destination-port 22;
protocol tcp;
}
then {
accept;
}
}
}
}
}
Table Families
inet (dual-stack), ip, ip6, arp, bridge, netdev.
Chain Types
filter, nat, route.
Hooks
input, output, forward, prerouting, postrouting, ingress, egress.
Match Types (from block)
| Config key | Match | Example |
|---|---|---|
source-address |
IP prefix | source-address 10.0.0.0/8; |
destination-address |
IP prefix | destination-address 192.168.1.0/24; |
source-port |
Port or range | source-port 1024-65535; |
destination-port |
Port or range | destination-port 22; or destination-port 80,443; |
protocol |
L4 protocol | protocol tcp; |
input-interface |
Interface name | input-interface eth0; |
output-interface |
Interface name | output-interface "l2tp*"; |
icmp-type |
ICMP type (name or number) | icmp-type echo-request; |
icmpv6-type |
ICMPv6 type (name or number) | icmpv6-type nd-neighbor-solicit; |
connection-state |
Conntrack states | connection-state established,related; |
connection-mark |
Mark value/mask | connection-mark 0x10/0xff; |
mark |
Packet mark value/mask | mark 0x10/0xff; |
dscp |
DSCP value (name or number) | dscp ef; |
tcp-flags |
TCP header flags | tcp-flags syn; |
source-address @set |
Named set lookup | source-address @blocked; |
ICMP Type Names
Symbolic names for icmp-type: echo-reply, destination-unreachable,
source-quench, redirect, echo-request, router-advertisement,
router-solicitation, time-exceeded, parameter-problem,
timestamp-request, timestamp-reply, info-request, info-reply,
address-mask-request, address-mask-reply. Numeric values (0-255)
are also accepted.
Symbolic names for icmpv6-type: destination-unreachable, packet-too-big,
time-exceeded, parameter-problem, echo-request, echo-reply,
mld-listener-query, mld-listener-report, mld-listener-done,
nd-router-solicit, nd-router-advert, nd-neighbor-solicit,
nd-neighbor-advert, nd-redirect, mld2-listener-report.
Numeric values (0-255) are also accepted.
Interface Wildcard
A trailing * on an interface name produces a prefix match. For example,
input-interface "l2tp*" matches any interface whose name starts with l2tp
(l2tp0, l2tp1, l2tp-peer42, etc.). Without the *, the match is exact.
Action Types (then block)
| Config key | Action | Example |
|---|---|---|
accept |
Accept packet | accept; |
drop |
Drop packet | drop; |
reject |
Reject with ICMP | reject { with icmp; code 3; } |
jump |
Jump to chain | jump helper; |
goto |
Goto chain | goto cleanup; |
return |
Return from chain | return; |
snat |
Source NAT | snat { to "10.0.0.1"; } |
dnat |
Destination NAT | dnat { to "10.1.1.1:8080"; } |
masquerade |
Masquerade | masquerade; or masquerade { port-range "1024-65535"; } or masquerade { random; } |
redirect |
Redirect to port | redirect { to 8080; } |
notrack |
Disable conntrack | notrack; |
flow-offload |
Hardware offload | flow-offload { flowtable ft0; } |
mark-set |
Set packet mark | mark-set { value 0x10; } |
connection-mark-set |
Set connmark | connection-mark-set { value 0x20/0xff; } |
dscp-set |
Set DSCP | dscp-set 46; |
tcp-mss-set |
Clamp TCP MSS | tcp-mss-set 1400; |
counter |
Count packets/bytes | counter; |
log |
Log packet | log { prefix "DROPPED"; } |
limit-rate |
Rate limit | limit-rate { rate 10/second; burst 5; } |
exclude |
Skip NAT (Return) | exclude; |
NAT Exclude
In a NAT chain, exclude emits a Return verdict so matched traffic skips
the NAT translation. This replaces the VyOS nat destination rule N exclude
pattern.
firewall {
backend nft;
table nat-rules {
family ip;
chain prerouting {
type nat;
hook prerouting;
priority -100;
policy accept;
term skip-local {
from {
destination-address 10.0.0.0/8;
}
then {
exclude;
}
}
term dnat-web {
from {
destination-port 80;
}
then {
dnat { to "10.1.1.1"; }
}
}
}
}
}
SNAT Address Ranges
SNAT and DNAT accept address ranges for pool-based NAT:
then {
snat { to "10.0.0.1-10.0.0.10"; }
}
Named Sets
firewall {
backend nft;
table wan {
family inet;
set blocked {
type ipv4;
element 10.0.0.1;
element 10.0.0.2 { timeout 3600; }
}
chain input {
type filter;
hook input;
priority 0;
policy drop;
term block-list {
from {
source-address @blocked;
}
then {
drop;
}
}
}
}
}
Global Options
The global-options container provides keyword toggles for common network
security defaults. Each keyword maps to a kernel sysctl. At
config apply time, the firewall component emits these as sysctl defaults via
EventBus. Explicit sysctl { setting { ... } } entries always override
global-options (three-layer priority: config > transient > default).
firewall {
backend nft;
global-options {
all-ping enable;
syn-cookies enable;
source-validation strict;
log-martians enable;
}
}
| Keyword | Sysctl | enable | disable |
|---|---|---|---|
all-ping |
net.ipv4.icmp_echo_ignore_all |
0 (allow) | 1 (ignore) |
broadcast-ping |
net.ipv4.icmp_echo_ignore_broadcasts |
0 (allow) | 1 (ignore) |
syn-cookies |
net.ipv4.tcp_syncookies |
1 | 0 |
receive-redirects |
net.ipv4.conf.all.accept_redirects |
1 | 0 |
send-redirects |
net.ipv4.conf.all.send_redirects |
1 | 0 |
source-validation |
net.ipv4.conf.all.rp_filter |
disable=0, strict=1, loose=2 | - |
log-martians |
net.ipv4.conf.all.log_martians |
1 | 0 |
ipv6-receive-redirects |
net.ipv6.conf.all.accept_redirects |
1 | 0 |
ipv6-src-route |
net.ipv6.conf.all.accept_source_route |
1 | 0 |
Note: all-ping and broadcast-ping have inverted semantics because the
underlying sysctl controls "ignore" behavior.
IRR Prefix-List Filtering
Firewall rules can match traffic by ASN or AS-SET using IRR-resolved prefix
lists. The firewall-irr plugin resolves references via the IRR whois client,
caches results in zefs, and populates nftables interval sets.
Operator Workflow
- Fetch prefix data:
update firewall irr asn 13335 - Inspect cached data:
show firewall irr - Commit config with
source-asn 13335in a term's from-block - Refresh all cached entries:
update firewall irr all
Config Leaves
| Leaf | Type | Description |
|---|---|---|
source-asn |
uint32 (1-4294967294) | Match source address against IRR-resolved prefixes for this ASN |
source-as-set |
string | Match source address against IRR-resolved prefixes for this AS-SET |
destination-asn |
uint32 (1-4294967294) | Match destination address against IRR-resolved prefixes for this ASN |
destination-as-set |
string | Match destination address against IRR-resolved prefixes for this AS-SET |
IRR Policy
firewall {
irr {
server whois.radb.net;
peeringdb-url https://www.peeringdb.com;
refresh-interval 0; /* 0 = manual only; 60-86400 = auto-refresh seconds */
}
}
Config commit rejects if a referenced ASN/AS-SET has no cached prefix data, with an actionable error naming the missing entry and the command to run.
Auto-refresh (when refresh-interval > 0) is fail-closed: a failed IRR query
preserves the last-good cache and logs an error.
Per-Interface Source Validation
Bind an AS-SET to a customer-facing interface. Packets arriving on that interface with source addresses not in the AS-SET's IRR-resolved prefixes are dropped (ingress source validation, BCP 38).
firewall {
irr {
interface eth1 {
source-as-set AS-CUSTOMER-A;
}
interface eth2 {
source-as-set AS-CUSTOMER-B;
}
}
}
The plugin generates a ze_irr_iface table with a prerouting base chain.
For each bound interface, accept terms match input-interface + source-address
in set for both IPv4 and IPv6, followed by a drop term for that interface.
Unconfigured interfaces pass through unfiltered (chain policy accept).
Same fail-closed semantics apply: config commit rejects if any bound AS-SET has no cached prefix data. Removing an interface binding removes its filter on the next apply.
CLI
| Command | Description |
|---|---|
ze firewall show |
Display all firewall tables and rules |
ze firewall counters |
Show per-term packet and byte counters |
show firewall irr |
Show IRR filter status for all cached entries |
show firewall irr prefix <name> |
List cached prefixes for an ASN or AS-SET |
update firewall irr asn <N> |
Fetch/refresh IRR prefix-list for an ASN |
update firewall irr as-set <name> |
Fetch/refresh IRR prefix-list for an AS-SET |
update firewall irr all |
Refresh all cached IRR entries |
Lifecycle
The firewall component registers with ze's engine via registry.Register.
On boot and config reload, the reactor parses the firewall { } section,
loads the selected backend, and calls Apply([]Table). On failure,
sdk.Journal triggers a rollback to the previous state.