EDIT: If you are looking for answers, read bold-fonted part.
In fact, it's best if you show us your config:
Sorry I didn't do so earlier, because my network topology is quite complicated. I have removed irrelevant parts:
Main router (PVE VM)
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 ''
config device
option name 'br-lan'
option type 'bridge'
list ports 'eth0'
list ports 'eth2'
list ports 'eth3'
list ports 'eth4'
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 '64'
option delegate '0'
config interface 'wan'
option device 'eth1'
option proto 'dhcp'
option peerdns '0'
list dns '127.0.0.1'
config interface 'wan6'
option device 'eth1'
option proto 'dhcpv6'
option reqaddress 'try'
option reqprefix 'auto'
option peerdns '0'
list dns '::1'
config interface 'guest'
option proto 'static'
option device 'br-lan.2'
option ipaddr '192.168.2.1'
option netmask '255.255.255.0'
option delegate '0'
config interface 'IoT'
option proto 'static'
option device 'br-lan.3'
option ipaddr '192.168.3.1'
option netmask '255.255.255.0'
option delegate '0'
config bridge-vlan
option device 'br-lan'
option vlan '1'
list ports 'eth0'
list ports 'eth2'
list ports 'eth3:t'
list ports 'eth4'
config bridge-vlan
option device 'br-lan'
option vlan '2'
list ports 'eth0:t'
list ports 'eth3:t'
config bridge-vlan
option device 'br-lan'
option vlan '3'
list ports 'eth0:t'
list ports 'eth3:t'
Dumb AP: (its wan
is connected to Main router's eth3
)
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 ''
config device
option name 'br-lan'
option type 'bridge'
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.2'
option netmask '255.255.255.0'
option gateway '192.168.1.1'
list dns '192.168.1.1'
option delegate '0'
config bridge-vlan
option device 'br-lan'
option vlan '1'
list ports 'lan1'
list ports 'lan2'
list ports 'lan3'
list ports 'wan:t'
config bridge-vlan
option device 'br-lan'
option vlan '2'
list ports 'wan:t'
config bridge-vlan
option device 'br-lan'
option vlan '3'
list ports 'wan:t'
config interface 'guest'
option proto 'static'
option device 'br-lan.2'
option delegate '0'
config interface 'IoT'
option proto 'static'
option device 'br-lan.3'
option delegate '0'
Thanks for the explanation! I am still a little confused:
the VLAN tag (on a tagged interface) is stripped when frames have entered through the ethernet port.
I am not sure I can follow this. On the main router, eth2
(part of br-lan
) and eth1
(wan
) are both physical ethernet ports that are passed through to Openwrt, if VLAN tag is stripped when a frame enters through eth2
(which is an ethernet port), then it seems to me that it is not doing anything at all because the tag gets added at eth2
and then it gets removed at eth2
as well (when entering, i.e. ingress). My understanding is that the tag should remain until it is stripped when leaving an untagged port, which leads to the problem that, if a tag 1
gets added at eth2
, where does it get removed before leaving at eth1
?
EDIT: I have done some more reading, and have a temporary explanation:

As shown in graph, data go from Port 0
to CPU Port
which is Port 8
then goes through RAM to CPU, I guess Linux's kernel handles (or consumes) the VLAN tag, and then the data goes out from CPU to Port 8
then out through Port 3
without VLAN tag. If this is wrong, please point it out.
The frame from the switch is received by the CPU’s Ethernet controller, and the driver calls netif_receive_skb() to pass the frame to the network stack in the normal way. eth_type_trans() is called to determine the Ether Type of the frame. As part of eth_type_trans(), a check is made to see if the ingress interface is a DSA master interface, i.e. netdev_uses_dsa(). If so, tagged frames are expected. The tag protocol receiver function is then invoked on th frame. This extracts the information from the tag, and then removes the tag from the frame. If the switch ingress port is valid, the DSA slave interface is determined, and the ingress interface is updated in the skb to point to the slave device. The frame is then again passed to the network stack using netif_receive_skb(). This time the true Ether Type can be extracted from the frame, and the frame is passed on for IP processing, etc. The transmit path is similar. The slaves transmit function invokes the tagger transmit function. It inserts the switch tag, and then calls the master interface’s transmit function via dev_queue_xmit().
VLANs don't apply to (normal) wifi. It can apply for less common wifi scenarios involving some mesh configurations and point-to-point radio links (these are not normal AP modes).
Does this mean that wifi does not "see" the VLAN tag at all and send it as is? Or do wifi strip VLAN tag when sending data out?
EDIT: I may have figured this part out, according to https://openwrt.org/docs/guide-user/network/dsa/converting-to-dsa, it seems that wireless interfaces act as untagged ports.
I'm not sue what you mean by this. Can you be more specific?
Right now, on the main router, I am using br-lan.1
for interface lan
, br-lan.2
for interface guest
, br-lan.3
for interface IoT
.
My original plan was to use br-lan
for interface lan
, but it doesn't work. I'm just curious why.
EDIT: I think I figured this one out too. When using DSA based openwrt, there is actually default VLAN setting enabled like this:

When I manually setup vlan filtering, the default setting is removed, so I have to configure lan
interface's vlan myself.
OK at least now I can sleep well. Until someone points out that I actually got it all wrong lol.