netgen is a command-line tool that turns a single YAML file describing your home network into the config and firewall scripts required by sbnMerlin. Instead of editing sbnMerlin.conf and the iptables rules by hand you declare zones, devices, and access policies in one place — netgen validates the config and pushes the generated files to your router.
sbnMerlin is a script for Asus-WRT Merlin firmware that creates isolated network segments (IoT, guest, kids, work, …) backed by per-zone DHCP and iptables rules. netgen is a companion tool that manages its configuration files. Credits to janico82 for the fantastic script! If you use sbnMerlin, consider sending him a donation.
Probably not. sbnMerlin by itself is a great tool that likely covers all your needs. If you're happy with your zones and rarely change anything, keep it that way! However, if, like me,
- you like granular control over your devices
- you find yourself keeping an excel with mac/ip address mappings
- you get dizzy from the contents of
br5_staticlist - you want to have an up to date diagram of your setup
You may want to give it a go! The tool fits seamlessly with the sbnMerlin features. It works best if you use different IP subnets for your zones. You can already easily configure internet, one way access and router access. netgen makes it easy to use mac addresses to assign static IPs to some of your devices - outside of the DHCP range but within the subnet of the bridge. You can then easily create granular policies. For instance:
- internet is blocked for IoT except for the vacuum cleaner
- chromecast may reach jellyfin on the mediaserver
- guests are isolated but can still reach the nas on another bridge
or whatever else you need. See examples for an idea of what that may look like.
Tip
Find the mac addresses of connected devices via: /jffs/scripts/sbnMerlin list-clients. Or, from outside the router: ssh www.asusrouter.com /jffs/scripts/sbnMerlin list-clients.
- Asus router running Asus-WRT Merlin with sbnMerlin installed and working
- SSH access to the router with key authentication
Download a binary from the Releases page, or, if you have go 1.26.1+:
go install github.com/arner/netgen/cmd/netgen@latestTip
If you've already configured sbnMerlin, scroll down to the netgen recover command.
It attempts to construct a network.yaml from your existing configuration!
- Copy the example that matches your router model (see examples/) to
network.yaml - Edit
network.yaml— setrouter.ssh_host, updatewan, adjust bridge numbers, and replace the placeholder MACs and IPs with your real devices - Run
netgen validateto catch errors before generating anything - Run
netgen generate --dry-runto preview the output without writing files - Run
netgen generateto write the files toout/ - Run
netgen push --applyto generate, copy, and apply the config in one go
Use at your own risk, obviously. It's always a good idea to backup your config and check the output of the tool before pushing it to the router.
Three (contrived) examples of network setups can be found in the examples directory: one for each type of router that is supported by sbnMerlin. The .yaml files demonstrate the features of netgen. See the output directories for the resulting sbnMerlin config.
| File | Models | Highlights |
|---|---|---|
| examples/dualband.yaml | RT-AX86U, RT-AX88U, RT-AX58U, and other dual-band models (sbnMerlin default) | PPPoE WAN with failover, wired LAN port in IoT zone, per-device DNS override, internet whitelist |
| examples/triband.yaml | GT-AXE11000, RT-BE96U | Work zone with router access, IoT zone with Pi-hole DNS, Kids zone with HTTP/HTTPS-only internet |
| examples/quadband.yaml | GT-AXE16000, GT-BE98 | Dual DNS (dns1 + dns2 fallback), mDNS via extra_rules, high-security 6 GHz-only zone |
Each file opens with a comment block showing the bridge assignments and WiFi band mapping for that router family.
A complete annotated example is in examples/. The tables below list every supported field.
| Field | Type | Default | Description |
|---|---|---|---|
ssh_host |
string | — | SSH target used by push (e.g. admin@192.168.1.1) |
wan |
string or list | — | WAN interface name(s). Run nvram get wan0_ifname on the router to confirm. Common values: eth0 (cable/fibre), ppp0 (PPPoE). A single interface can be written as a scalar (wan: eth0) or as a one-element list. |
wan_rule_position |
int | 0 (unset) | Position at which internet whitelist rules are inserted into the FORWARD chain (-I FORWARD N). Required when using PPPoE (ppp0) if a TCPMSS clamping rule is present — set to a value just above the TCPMSS line number. Check with: iptables -L FORWARD --line-numbers | grep TCPMSS on the router. |
| Field | Type | Default | Description |
|---|---|---|---|
name |
string | — | Zone identifier; referenced in exceptions |
bridge |
string | — | sbnMerlin bridge name. Valid range: br3–br9, br11–br14, br17–br18, br21–br24, br27–br29. Use br0 for the main LAN (netgen does not generate config for it, but devices can be referenced in exceptions). |
enabled |
bool | true |
Set false to emit only brN_enabled=0 and skip all other settings — useful when decommissioning a bridge. |
subnet |
CIDR | — | Zone subnet (e.g. 10.0.5.0/24). The gateway is derived as the first host address (.1). |
dhcp_start |
IP | — | First address of the DHCP pool |
dhcp_end |
IP | — | Last address of the DHCP pool |
ifnames |
list | — | Extra interfaces to bridge into this zone (e.g. eth1 to wire in a LAN port). Do not use eth0 (WAN) or wl*.1 (main LAN SSID). |
dns1 |
IP | — | Zone-wide primary DNS override (e.g. a Pi-hole on your main LAN) |
dns2 |
IP | — | Zone-wide secondary DNS (e.g. 1.1.1.1 as a fallback if dns1 is down) |
ap_isolate |
bool | false |
Prevent WiFi clients in this zone from talking to each other. Exceptions can still open specific flows. |
allow_internet |
bool | false |
Allow all devices in this zone to reach the internet. For per-device control, leave this false and use the internet field on each device. |
allow_onewayaccess |
bool | false |
Allow devices on the main LAN (br0) to initiate connections into this zone. Traffic in the other direction is still blocked. |
allow_routeraccess |
bool | false |
Allow zone devices to reach services running on the router itself (e.g. a WireGuard server or the admin panel). |
extra_rules |
list | — | Verbatim iptables rules appended to the filter script. Must match the rule format sbnMerlin accepts (e.g. "-I INPUT -i br17 -p udp --dport 5353 -j ACCEPT"). |
Devices are listed under their zone. Each device gets a static DHCP lease.
| Field | Type | Description |
|---|---|---|
name |
string | Device name used in exceptions. Must match [a-zA-Z][a-zA-Z0-9_-]{0,19}. |
mac |
string | MAC address. Any common format is accepted; normalised to uppercase colon-separated internally. |
ip |
IP | Static lease address. Must be within the zone's subnet and outside the DHCP pool (or a warning is issued). |
dns |
IP | Per-device DNS override. Overrides dns1 for this device only. |
internet |
— | Internet access policy for this device (see variants below). Ignored when the zone already has allow_internet: true. |
internet field variants
| Value | Meaning |
|---|---|
absent or false |
No internet access |
true |
Unrestricted internet (all protocols and ports) |
{proto: tcp, dport: 443} |
Whitelist a single port |
[{proto: tcp, dport: 80}, {proto: tcp, dport: 443}] |
Whitelist multiple ports |
Valid protocols: tcp, udp, icmp. Ports can be a number (443) or a range (8000:8080).
Exceptions open specific traffic flows that would otherwise be blocked (e.g. when ap_isolate is on, or between zones).
| Field | Type | Description |
|---|---|---|
from |
string | Source device name |
to |
string | Destination device name |
proto |
string | Optional: restrict to tcp, udp, or icmp |
dport |
number / range | Optional: restrict to a destination port or range |
All commands accept:
| Flag | Default | Description |
|---|---|---|
--config <path> |
network.yaml |
Path to the network config file |
--out-dir <path> |
out |
Directory where generated files are written |
Checks the config against all validation rules and prints any issues. Exits with code 1 if errors are found. Run this before generate or push.
netgen validate
netgen validate --config examples/dualband.yamlGenerates sbnMerlin.conf and one br*_iptables.filter per active zone that has rules, written to --out-dir. With --dry-run, prints to stdout instead of writing files.
It also generates a *.puml PlantUML diagram. Convert it to png for a diagram of your network, for instance via plantuml.com.
netgen generate
netgen generate --dry-run
netgen generate --out-dir /tmp/previewGenerates the files, writes them to --out-dir, and copies them to the router via scp. Destination paths on the router:
sbnMerlin.conf→/jffs/addons/sbnMerlin.d/sbnMerlin.confbr*_iptables.filter→/jffs/addons/sbnMerlin.d/cscripts/
| Flag | Description |
|---|---|
--dry-run |
Print the scp/ssh commands without executing them |
--apply |
After pushing, run sbnMerlin run-config sc on the router via SSH |
netgen push
netgen push --apply
netgen push --dry-runTip
Before running netgen recover, copy the files from the router.
mkdir -p out/cscripts && cd out
scp -O www.asusrouter.com:/jffs/addons/sbnMerlin.d/sbnMerlin.conf .
scp -O 'www.asusrouter.com:/jffs/addons/sbnMerlin.d/cscripts/*' cscripts/Recover reads a generated or copied out/ directory (containing sbnMerlin.conf and brN_iptables.filter files) and reconstructs a network.yaml. Useful for importing an existing sbnMerlin setup into netgen.
Review all # TODO comments in the output — some information (e.g. ssh_host, WAN interface when no internet whitelist rules are present) cannot be recovered from the generated files.
| Flag | Default | Description |
|---|---|---|
--dir <path> |
out |
Directory containing sbnMerlin.conf and filter files |
--output <path> |
stdout | Write the recovered YAML to a file instead of stdout |
netgen recover # print to stdout
netgen recover --dir /tmp/sbnMerlin # read from custom dir
netgen recover --output network.yaml # write to filenetwork.yaml ──► netgen validate
│
▼
netgen generate
├── out/sbnMerlin.conf
└── out/cscripts/br*_iptables.filter
│
▼
netgen push (scp to router)
│
▼
sbnMerlin run-config
netgen reads the YAML, validates it against bridge numbering rules and IP constraints, then generates the config file and per-zone iptables filter scripts that sbnMerlin expects. The filter scripts contain the FORWARD rules for internet whitelists and cross-zone exceptions, and any verbatim extra_rules.
Bridge numbers. The valid bridge range depends on your router model. Using a bridge index outside the model's supported set will cause sbnMerlin errors. Pick the example for your model as the starting point — bridge numbers and band mappings are listed in the comment block at the top.
BSB bridges (br1–br4 depending on model) are basic single-band bridges whose IP and DHCP settings are set in NVRAM by sbnMerlin directly. netgen does not generate configuration for them and they are not listed as valid bridge values in zones.
Main LAN (br0) is always managed by the router firmware. Define a br0 zone only if you need to reference main-LAN devices in exceptions; no config is generated for it.


