I would not trust config file but step by step instructions seemed reasonable. For debugging I simplified the architecture to this.
IOW, the router acts as core switch right now, only default lan plus one more vlan:
and I only setup routed-iot on AP2 for now. With the result that
- lan on vlan 1 works like it should (thankfully)
- routed-iot on vlan 10 cannot even ping the router with static IP (when connecting to its SSID) and DHCP fails completely. So happy to take you up on your offer

Router
(no wifi; what it calls wan is really a 4th gige switch port which I bridged into the switch as I am using the SFP port named eth2 as WAN)
root@bpir4:~# ubus call system board
{
"kernel": "6.6.73",
"hostname": "bpir4",
"system": "ARMv8 Processor rev 0",
"model": "Bananapi BPI-R4",
"board_name": "bananapi,bpi-r4",
"rootfs_type": "squashfs",
"release": {
"distribution": "OpenWrt",
"version": "24.10.0",
"revision": "r28427-6df0e3d02a",
"target": "mediatek/filogic",
"description": "OpenWrt 24.10.0 r28427-6df0e3d02a",
"builddate": "1738624177"
}
}
root@bpir4:~# cat /etc/config/network
config interface 'loopback'
option device 'lo'
option proto 'static'
option ipaddr '127.0.0.1'
option netmask '255.0.0.0'
config globals 'globals'
option ula_prefix 'fdee:5c1c:2872::/48'
option packet_steering '1'
config device
option name 'br-lan'
option type 'bridge'
list ports 'eth1'
list ports 'lan1'
list ports 'lan2'
list ports 'lan3'
list ports 'wan'
config interface 'lan'
option device 'br-lan.1'
option proto 'static'
option ipaddr '192.168.1.1'
option netmask '255.255.255.0'
option ip6assign '60'
config device
option name 'br-wan'
option type 'bridge'
list ports 'eth2'
config device
option name 'wan'
option macaddr 'ca:28:51:5f:09:2b'
config device
option name 'eth2'
option macaddr 'ca:28:51:5f:09:2b'
config interface 'wan'
option device 'br-wan'
option proto 'dhcp'
config interface 'wan6'
option device 'br-wan'
option proto 'dhcpv6'
option force_link '1'
option reqaddress 'try'
option reqprefix 'auto'
[skip wireguard]
config bridge-vlan
option device 'br-lan'
option vlan '1'
list ports 'eth1:u*'
list ports 'lan1:u*'
list ports 'lan2:u*'
list ports 'lan3:u*'
list ports 'wan:u*'
config bridge-vlan
option device 'br-lan'
option vlan '10'
list ports 'eth1:t'
list ports 'lan1:t'
list ports 'lan3:t'
list ports 'wan:t'
config interface 'routed_iot'
option proto 'static'
option device 'br-lan.10'
option ipaddr '192.168.10.1'
option netmask '255.255.255.0'
config dnsmasq
option domainneeded '1'
option localise_queries '1'
option rebind_protection '1'
option rebind_localhost '1'
option local '/lan/'
option domain 'lan'
option expandhosts '1'
option cachesize '1000'
option authoritative '1'
option readethers '1'
option leasefile '/tmp/dhcp.leases'
option localservice '1'
option ednspacket_max '1232'
list interface 'lan'
list interface 'vpn'
list server '/mask.icloud.com/'
list server '/mask-h2.icloud.com/'
list server '/use-application-dns.net/'
list server '127.0.0.1#5053'
list server '127.0.0.1#5054'
option doh_backup_noresolv '-1'
option noresolv '1'
list doh_backup_server '/mask.icloud.com/'
list doh_backup_server '/mask-h2.icloud.com/'
list doh_backup_server '/use-application-dns.net/'
list doh_backup_server '127.0.0.1#5053'
list doh_backup_server '127.0.0.1#5054'
list doh_server '127.0.0.1#5053'
list doh_server '127.0.0.1#5054'
config dhcp 'lan'
option interface 'lan'
option start '100'
option limit '150'
option leasetime '12h'
option dhcpv4 'server'
option dhcpv6 'server'
option ra 'server'
list ra_flags 'managed-config'
list ra_flags 'other-config'
config dhcp 'wan'
option interface 'wan'
option ignore '1'
config odhcpd 'odhcpd'
option maindhcp '0'
option leasefile '/tmp/hosts/odhcpd'
option leasetrigger '/usr/sbin/odhcpd-update'
option loglevel '4'
[... skip static ips...]
config dhcp 'routed_iot'
option interface 'routed_iot'
option start '100'
option limit '150'
option leasetime '12h'
config forwarding
option src 'routed_iot' # Allow routed_iot to reach WAN
option dest 'wan'
root@bpir4:~# cat /etc/config/firewall
config defaults
option input 'REJECT'
option output 'ACCEPT'
option forward 'REJECT'
option synflood_protect '1'
option flow_offloading '1'
option flow_offloading_hw '1'
config zone
option name 'lan'
option input 'ACCEPT'
option output 'ACCEPT'
option forward 'ACCEPT'
option masq '1'
list network 'lan'
list network 'vpn'
config zone
option name 'wan'
option input 'REJECT'
option output 'ACCEPT'
option forward 'REJECT'
option masq '1'
option mtu_fix '1'
list network 'wan'
list network 'wan6'
config forwarding
option src 'lan'
option dest 'wan'
config rule
option name 'Allow-DHCP-Renew'
option src 'wan'
option proto 'udp'
option dest_port '68'
option target 'ACCEPT'
option family 'ipv4'
config rule
option name 'Allow-Ping'
option src 'wan'
option proto 'icmp'
option icmp_type 'echo-request'
option family 'ipv4'
option target 'ACCEPT'
config rule
option name 'Allow-IGMP'
option src 'wan'
option proto 'igmp'
option family 'ipv4'
option target 'ACCEPT'
config rule
option name 'Allow-DHCPv6'
option src 'wan'
option proto 'udp'
option dest_port '546'
option family 'ipv6'
option target 'ACCEPT'
config rule
option name 'Allow-MLD'
option src 'wan'
option proto 'icmp'
option src_ip 'fe80::/10'
list icmp_type '130/0'
list icmp_type '131/0'
list icmp_type '132/0'
list icmp_type '143/0'
option family 'ipv6'
option target 'ACCEPT'
config rule
option name 'Allow-ICMPv6-Input'
option src 'wan'
option proto 'icmp'
list icmp_type 'echo-request'
list icmp_type 'echo-reply'
list icmp_type 'destination-unreachable'
list icmp_type 'packet-too-big'
list icmp_type 'time-exceeded'
list icmp_type 'bad-header'
list icmp_type 'unknown-header-type'
list icmp_type 'router-solicitation'
list icmp_type 'neighbour-solicitation'
list icmp_type 'router-advertisement'
list icmp_type 'neighbour-advertisement'
option limit '1000/sec'
option family 'ipv6'
option target 'ACCEPT'
config rule
option name 'Allow-ICMPv6-Forward'
option src 'wan'
option dest '*'
option proto 'icmp'
list icmp_type 'echo-request'
list icmp_type 'echo-reply'
list icmp_type 'destination-unreachable'
list icmp_type 'packet-too-big'
list icmp_type 'time-exceeded'
list icmp_type 'bad-header'
list icmp_type 'unknown-header-type'
option limit '1000/sec'
option family 'ipv6'
option target 'ACCEPT'
config rule
option name 'Allow-IPSec-ESP'
option src 'wan'
option dest 'lan'
option proto 'esp'
option target 'ACCEPT'
config rule
option name 'Allow-ISAKMP'
option src 'wan'
option dest 'lan'
option dest_port '500'
option proto 'udp'
option target 'ACCEPT'
config rule
option name 'Allow-WireGuard'
option src 'wan'
option dest_port '51820'
option proto 'udp'
option target 'ACCEPT'
config zone
option name 'routed_iot'
option input 'REJECT'
option output 'ACCEPT'
option forward 'ACCEPT'
list network 'routed_iot'
config rule
option name 'Allow-routed-iot-DNS'
option src 'routed_iot'
option dest_port '53'
option target 'ACCEPT'
list proto 'tcp'
list proto 'udp'
config rule
option name 'Allow-routed-iot-DHCP'
list proto 'udp'
option src 'routed_iot'
option dest_port '67-68'
option target 'ACCEPT'
ap2
(eth0 is its uplink, DHCPd is off so omitting the file)
{
"kernel": "6.6.73",
"hostname": "mt3000",
"system": "ARMv8 Processor rev 4",
"model": "GL.iNet GL-MT3000",
"board_name": "glinet,gl-mt3000",
"rootfs_type": "squashfs",
"release": {
"distribution": "OpenWrt",
"version": "24.10.0",
"revision": "r28427-6df0e3d02a",
"target": "mediatek/filogic",
"description": "OpenWrt 24.10.0 r28427-6df0e3d02a",
"builddate": "1738624177"
}
}
config interface 'loopback'
option device 'lo'
option proto 'static'
option ipaddr '127.0.0.1'
option netmask '255.0.0.0'
config globals 'globals'
option ula_prefix 'fd02:d700:86e7::/48'
option packet_steering '1'
config device
option name 'br-lan'
option type 'bridge'
list ports 'eth0'
list ports 'eth1'
config interface 'lan'
option device 'br-lan.1'
option proto 'static'
option ipaddr '192.168.1.6'
option netmask '255.255.255.0'
option ip6assign '60'
list dns '192.168.1.1'
option gateway '192.168.1.1'
config bridge-vlan
option device 'br-lan'
option vlan '1'
list ports 'eth0:u*'
list ports 'eth1:u*'
config bridge-vlan
option device 'br-lan'
option vlan '10'
list ports 'eth0:t'
config interface 'br_routed_iot'
option proto 'none'
option device 'br-lan.10'
option type 'bridge'
config wifi-device 'radio0'
option type 'mac80211'
option path 'platform/soc/18000000.wifi'
option cell_density '0'
option country 'CH'
config wifi-device 'radio1'
option type 'mac80211'
option path 'platform/soc/18000000.wifi+1'
option channel 'auto'
option band '5g'
option htmode 'HE160'
option cell_density '0'
option country 'CH'
config wifi-iface 'wifinet0'
option device 'radio0'
option mode 'ap'
option ssid 'KG'
option encryption 'psk2'
option network 'lan'
option ieee80211r '1'
option ft_over_ds '0'
option ft_psk_generate_local '1'
config wifi-iface 'wifinet1'
option device 'radio1'
option mode 'ap'
option ssid 'KG'
option encryption 'psk2'
option network 'lan'
option ieee80211r '1'
option ft_over_ds '0'
option ft_psk_generate_local '1'
config wifi-iface 'wifinet2'
option device 'radio1'
option mode 'ap'
option ssid 'KG-routed-iot'
option encryption 'psk2'
option network 'br_routed_iot'
config defaults
option syn_flood '1'
option input 'REJECT'
option output 'ACCEPT'
option forward 'REJECT'
config zone
option name 'lan'
option input 'ACCEPT'
option output 'ACCEPT'
option forward 'ACCEPT'
list network 'lan'
list network 'br_routed_iot'
config zone
option name 'wan'
option input 'REJECT'
option output 'ACCEPT'
option forward 'REJECT'
option masq '1'
option mtu_fix '1'
config forwarding
option src 'lan'
option dest 'wan'
config rule
option name 'Allow-DHCP-Renew'
option src 'wan'
option proto 'udp'
option dest_port '68'
option target 'ACCEPT'
option family 'ipv4'
config rule
option name 'Allow-Ping'
option src 'wan'
option proto 'icmp'
option icmp_type 'echo-request'
option family 'ipv4'
option target 'ACCEPT'
config rule
option name 'Allow-IGMP'
option src 'wan'
option proto 'igmp'
option family 'ipv4'
option target 'ACCEPT'
config rule
option name 'Allow-DHCPv6'
option src 'wan'
option proto 'udp'
option dest_port '546'
option family 'ipv6'
option target 'ACCEPT'
config rule
option name 'Allow-MLD'
option src 'wan'
option proto 'icmp'
option src_ip 'fe80::/10'
list icmp_type '130/0'
list icmp_type '131/0'
list icmp_type '132/0'
list icmp_type '143/0'
option family 'ipv6'
option target 'ACCEPT'
config rule
option name 'Allow-ICMPv6-Input'
option src 'wan'
option proto 'icmp'
list icmp_type 'echo-request'
list icmp_type 'echo-reply'
list icmp_type 'destination-unreachable'
list icmp_type 'packet-too-big'
list icmp_type 'time-exceeded'
list icmp_type 'bad-header'
list icmp_type 'unknown-header-type'
list icmp_type 'router-solicitation'
list icmp_type 'neighbour-solicitation'
list icmp_type 'router-advertisement'
list icmp_type 'neighbour-advertisement'
option limit '1000/sec'
option family 'ipv6'
option target 'ACCEPT'
config rule
option name 'Allow-ICMPv6-Forward'
option src 'wan'
option dest '*'
option proto 'icmp'
list icmp_type 'echo-request'
list icmp_type 'echo-reply'
list icmp_type 'destination-unreachable'
list icmp_type 'packet-too-big'
list icmp_type 'time-exceeded'
list icmp_type 'bad-header'
list icmp_type 'unknown-header-type'
option limit '1000/sec'
option family 'ipv6'
option target 'ACCEPT'
config rule
option name 'Allow-IPSec-ESP'
option src 'wan'
option dest 'lan'
option proto 'esp'
option target 'ACCEPT'
config rule
option name 'Allow-ISAKMP'
option src 'wan'
option dest 'lan'
option dest_port '500'
option proto 'udp'
option target 'ACCEPT'